json_web_token 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d9c63b5c2fa2302f1cee322ac44cb54eddda7538
4
- data.tar.gz: c2215846a69b4d8f3a4705dbedbbebfbb32253ae
3
+ metadata.gz: d1518a4e0fe92dc8de73a3ed606115ac89ef466b
4
+ data.tar.gz: 5d38d88d1307780db38343d0c23d315963fa376d
5
5
  SHA512:
6
- metadata.gz: a4eb15f1a8da6eded296e6165d1ed0ffe7d62b21f11116efc7108385f0202150511a0f87325fab3ec4b243318923759d241a607b3650126f83a5885a066c78df
7
- data.tar.gz: 5be8f29d618889f7315230b94b1b0694eb6c80d78d0006aec65cea4835d783af66f23e13a7d5a5ede42971a4145a5c44e25c19aeee3340dd1f981fb5d60bc673
6
+ metadata.gz: 781e08e80e4ec08fa00f02e7d7127bca5ba50ce83f2708c7e5135c53b4edeaac53891bac3a0fcc517112511210ab55fd38552e105db62bc631453f3f5df289c1
7
+ data.tar.gz: 4c55609063885e415f48ea97c9169076837551fdc2a779f80ee27d14442cdfaf487d3f3dcdeae9fca6ceb6000af210b81d12876e7697250f7ec7ccaff9d976bc
@@ -0,0 +1,11 @@
1
+ ## Changelog
2
+
3
+ ### 0.0.2 2015-07-11
4
+
5
+ * enhancements
6
+ * support RSASSA-PKCS-v1_5 algorithm
7
+
8
+ ### 0.0.1 2015-07-09
9
+
10
+ * initial
11
+ * support HMAC algorithm
data/Gemfile CHANGED
@@ -2,4 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
+ gem 'pry-byebug', '~> 3.1', require: false
5
6
  gem 'simplecov', '~> 0.10', require: false
data/README.md CHANGED
@@ -28,36 +28,99 @@ Token authentication of API requests to Rails via these popular gems
28
28
  Secure Cross-Origin Resource Sharing ([CORS][cors]) using the [rack-cors][rack-cors] gem
29
29
 
30
30
  ## Usage
31
- Create a JSON web token
31
+
32
+ ### JsonWebToken.create(claims, options)
33
+
34
+ Returns a JSON Web Token string
35
+
36
+ `claims` (required) string or hash
37
+
38
+ `options` (optional) hash
39
+
40
+ * **alg**, default: `HS256`
41
+ * **key** (required unless alg is 'none')
42
+
43
+ Example
32
44
 
33
45
  ```ruby
34
46
  require 'json_web_token'
35
47
 
36
- JsonWebToken.create(claims, options)
48
+ # sign with default algorithm, HMAC SHA256
49
+ jwt = JsonWebToken.create({foo: 'bar'}, key: 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C')
50
+
51
+ # sign with RSA SHA256 algorithm
52
+ options = {
53
+ alg: 'RSA256',
54
+ key: < RSA private key >
55
+ }
56
+
57
+ jwt = JsonWebToken.create({foo: 'bar'}, options)
58
+
59
+ # unsecured token (algorithm is 'none')
60
+ jwt = JsonWebToken.create({foo: 'bar'}, alg: 'none')
61
+
37
62
  ```
38
63
 
39
- Validate a JSON web token
64
+ ### JsonWebToken.validate(jwt, options)
65
+
66
+ Returns a JWT claims set string or hash, if the MAC signature is valid
67
+
68
+ `jwt` (required) is a JSON web token string
69
+
70
+ `options` (optional) hash
71
+
72
+ * **algorithm**, default: `HS256`
73
+ * **key** (required unless alg is 'none')
74
+
75
+ Example
40
76
 
41
77
  ```ruby
78
+ require 'json_web_token'
79
+
80
+ secure_jwt = 'eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt.cGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'
81
+
82
+ # verify with default algorithm, HMAC SHA256
83
+ claims = JsonWebToken.validate(secure_jwt, key: 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C')
84
+
85
+ # verify with RSA SHA256 algorithm
86
+ options = {
87
+ alg: 'RSA256',
88
+ key: < RSA public key >
89
+ }
90
+
42
91
  claims = JsonWebToken.validate(jwt, options)
92
+
93
+ # unsecured token (algorithm is 'none')
94
+
95
+ unsecured_jwt = 'eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt.'
96
+
97
+ claims = JsonWebToken.validate(unsecured_jwt, alg: 'none')
98
+
43
99
  ```
44
100
  ### Supported encryption algorithms
45
- The 2 REQUIRED JWT algorithms
46
101
 
47
- - HMAC using SHA-256 per [RFC 2104][rfc2104]
48
- - 'none' (unsecured)
102
+ alg Param Value | Digital Signature or MAC Algorithm
103
+ ------|------
104
+ HS256 | HMAC using SHA-256 per [RFC 2104][rfc2104]
105
+ HS384 | HMAC using SHA-384
106
+ HS512 | HMAC using SHA-512
107
+ RS256 | RSASSA-PKCS-v1_5 using SHA-256 per [RFC3447][rfc3447]
108
+ RS384 | RSASSA-PKCS-v1_5 using SHA-384
109
+ RS512 | RSASSA-PKCS-v1_5 using SHA-512
110
+ none | No digital signature or MAC performed (unsecured)
49
111
 
50
112
  ### Supported Ruby Versions
51
- Ruby 2.1 and up
113
+ Ruby 2.0 and up
52
114
 
53
115
  ### Limitations
54
116
  Future implementation may include these features:
55
117
 
56
- - RECOMMENDED or OPTIONAL encryption algorithms
57
- - Representation of a JWT as a JSON Web Encryption (JWE) [RFC 7516][rfc7516]
118
+ - additional RECOMMENDED or OPTIONAL encryption algorithms
119
+ - representation of a JWT as a JSON Web Encryption (JWE) [RFC 7516][rfc7516]
58
120
  - OPTIONAL nested JWTs
59
121
 
60
122
  [rfc2104]: http://tools.ietf.org/html/rfc2104
123
+ [rfc3447]: http://tools.ietf.org/html/rfc3447
61
124
  [rfc7515]: http://tools.ietf.org/html/rfc7515
62
125
  [rfc7516]: http://tools.ietf.org/html/rfc7516
63
126
  [rfc7518]: http://tools.ietf.org/html/rfc7518
@@ -4,7 +4,7 @@ require 'json_web_token/version'
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.author = 'Gary Fleshman'
7
- s.email = 'gf4cl@verizon.net'
7
+ s.email = 'gfleshman@newforge-tech.com'
8
8
  s.files = `git ls-files`.split("\n")
9
9
  s.homepage = 'https://github.com/garyf/json_web_token'
10
10
  s.name = 'json_web_token'
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
15
15
  s.license = 'MIT'
16
16
  # optional
17
17
  s.add_runtime_dependency 'json', '~> 1.8', '>= 1.8.3'
18
- s.add_development_dependency 'pry-byebug', '~> 3.1'
19
18
  s.add_development_dependency 'rspec', '~> 3.3'
20
19
  s.description = 'Ruby implementation of the JSON Web Token Standard Track RFC 4627'
20
+ s.required_ruby_version = '>= 2.0.0'
21
21
  end
@@ -0,0 +1,27 @@
1
+ require 'openssl'
2
+
3
+ module JsonWebToken
4
+ module Algorithm
5
+ module Common
6
+
7
+ SHA_BITS = [
8
+ '256',
9
+ '384',
10
+ '512'
11
+ ]
12
+
13
+ def validate_key(key, sha_bits)
14
+ validate_sha_bits(sha_bits)
15
+ validate_key_size(key, sha_bits)
16
+ end
17
+
18
+ def validate_sha_bits(sha_bits)
19
+ fail('Invalid sha_bits') unless SHA_BITS.include?(sha_bits)
20
+ end
21
+
22
+ def digest_new(sha_bits)
23
+ OpenSSL::Digest.new("sha#{sha_bits}")
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,47 +1,32 @@
1
+ require 'json_web_token/algorithm/common'
1
2
  require 'json_web_token/util'
2
- require 'openssl'
3
3
 
4
4
  module JsonWebToken
5
5
  module Algorithm
6
6
  module Hmac
7
7
 
8
- SHA_BITS = [
9
- '256',
10
- '384',
11
- '512'
12
- ]
8
+ extend JsonWebToken::Algorithm::Common
13
9
 
14
10
  module_function
15
11
 
16
12
  def signed(sha_bits, key, data)
17
- validate_params(key, sha_bits)
18
- OpenSSL::HMAC.digest(OpenSSL::Digest.new("sha#{sha_bits}"), key, data)
13
+ validate_key(key, sha_bits)
14
+ OpenSSL::HMAC.digest(digest_new(sha_bits), key, data)
19
15
  end
20
16
 
21
17
  def verified?(mac, sha_bits, key, data)
22
- validate_params(key, sha_bits)
18
+ validate_key(key, sha_bits)
23
19
  Util.constant_time_compare(mac, signed(sha_bits, key, data))
24
20
  end
25
21
 
26
22
  # private
27
23
 
28
- def validate_params(key, sha_bits)
29
- validate_sha_bits(sha_bits)
30
- validate_key_size(key, sha_bits)
31
- end
32
-
33
- def validate_sha_bits(sha_bits)
34
- fail('Invalid sha_bits') unless SHA_BITS.include?(sha_bits)
35
- end
36
-
37
24
  # http://tools.ietf.org/html/rfc7518#section-3.2
38
25
  def validate_key_size(key, sha_bits)
39
26
  fail('Invalid key') unless key && key.bytesize * 8 >= sha_bits.to_i
40
27
  end
41
28
 
42
- private_class_method :validate_params,
43
- :validate_sha_bits,
44
- :validate_key_size
29
+ private_class_method :validate_key_size
45
30
  end
46
31
  end
47
32
  end
@@ -0,0 +1,34 @@
1
+ require 'json_web_token/algorithm/common'
2
+
3
+ module JsonWebToken
4
+ module Algorithm
5
+ module Rsa
6
+
7
+ extend JsonWebToken::Algorithm::Common
8
+
9
+ KEY_BITS_MIN = 2048
10
+
11
+ module_function
12
+
13
+ def signed(sha_bits, key, data)
14
+ validate_key(key, sha_bits)
15
+ key.sign(digest_new(sha_bits), data)
16
+ end
17
+
18
+ def verified?(signature, sha_bits, key, data)
19
+ validate_key(key, sha_bits)
20
+ key.verify(digest_new(sha_bits), signature, data)
21
+ end
22
+
23
+ # private
24
+
25
+ # http://tools.ietf.org/html/rfc7518#section-3.3
26
+ # https://github.com/ruby/openssl/issues/5
27
+ def validate_key_size(key, sha_bits)
28
+ fail('Invalid private key') unless key && key.n.num_bits >= KEY_BITS_MIN
29
+ end
30
+
31
+ private_class_method :validate_key_size
32
+ end
33
+ end
34
+ end
@@ -1,33 +1,22 @@
1
1
  require 'json_web_token/algorithm/hmac'
2
+ require 'json_web_token/algorithm/rsa'
2
3
 
3
4
  module JsonWebToken
4
5
  module Jwa
5
6
 
6
- ALGORITHMS = /(HS)(256|384|512)?/i
7
+ ALGORITHMS = /(HS|RS)(256|384|512)?/i
7
8
  ALG_LENGTH = 5
8
9
 
9
10
  module_function
10
11
 
11
12
  def signed(algorithm, key, data)
12
13
  alg = validated_alg(algorithm)
13
- sha_bits = alg[:sha_bits]
14
- case alg[:kind]
15
- when 'hs'
16
- Algorithm::Hmac.signed(sha_bits, key, data)
17
- else
18
- fail('Unsupported algorithm')
19
- end
14
+ alg[:constant].signed(alg[:sha_bits], key, data)
20
15
  end
21
16
 
22
17
  def verified?(signature, algorithm, key, data)
23
18
  alg = validated_alg(algorithm)
24
- sha_bits = alg[:sha_bits]
25
- case alg[:kind]
26
- when 'hs'
27
- Algorithm::Hmac.verified?(signature, sha_bits, key, data)
28
- else
29
- false
30
- end
19
+ alg[:constant].verified?(signature, alg[:sha_bits], key, data)
31
20
  end
32
21
 
33
22
  # private
@@ -41,12 +30,21 @@ module JsonWebToken
41
30
  match = algorithm.match(ALGORITHMS)
42
31
  return unless match && match[0].length == ALG_LENGTH
43
32
  {
44
- kind: match[1].downcase,
45
- sha_bits: match[2]
33
+ constant: validated_constant(match[1].downcase),
34
+ sha_bits: match[2],
46
35
  }
47
36
  end
48
37
 
38
+ def validated_constant(str)
39
+ case str
40
+ when 'hs' then Algorithm::Hmac
41
+ when 'rs' then Algorithm::Rsa
42
+ else fail('Unsupported algorithm')
43
+ end
44
+ end
45
+
49
46
  private_class_method :validated_alg,
50
47
  :destructured_alg
48
+ :validated_constant
51
49
  end
52
50
  end
@@ -14,10 +14,9 @@ module JsonWebToken
14
14
  # http://tools.ietf.org/html/rfc7519#page-12
15
15
  def create(claims, options = {})
16
16
  message = validated_message(claims)
17
- key = options[:key]
18
17
  header = config_header(options)
19
18
  return Jws.unsecured_jws(header, message) if header[:alg] == 'none'
20
- Jws.message_signature(header, message, key)
19
+ Jws.message_signature(header, message, options[:key])
21
20
  end
22
21
 
23
22
  def validate(jwt, options = {})
@@ -29,7 +28,7 @@ module JsonWebToken
29
28
  # private
30
29
 
31
30
  def validated_message(claims)
32
- fail('Claims not provided') if !claims || claims.empty?
31
+ fail('Claims blank') if !claims || claims.empty?
33
32
  claims.to_json
34
33
  end
35
34
 
@@ -1,3 +1,3 @@
1
1
  module JsonWebToken
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
@@ -0,0 +1,126 @@
1
+ require 'json_web_token/algorithm/rsa'
2
+
3
+ module JsonWebToken
4
+ module Algorithm
5
+ describe Rsa do
6
+ context 'detect changed signing_input or MAC' do
7
+ let(:private_key) { OpenSSL::PKey::RSA.generate(Rsa::KEY_BITS_MIN) }
8
+ let(:public_key) { private_key.public_key }
9
+ let(:signing_input) { 'signing_input' }
10
+ let(:changed_signing_input) { 'changed_signing_input' }
11
+ shared_examples_for '#signed' do
12
+ it 'is #verified?' do
13
+ mac = Rsa.signed(sha_bits, private_key, signing_input)
14
+ expect(Rsa.verified? mac, sha_bits, public_key, signing_input).to be true
15
+ expect(Rsa.verified? mac, sha_bits, public_key, changed_signing_input).to be false
16
+
17
+ changed_mac = Rsa.signed(sha_bits, private_key, changed_signing_input)
18
+ expect(Rsa.verified? changed_mac, sha_bits, public_key, signing_input).to be false
19
+ end
20
+ end
21
+
22
+ context 'RS256' do
23
+ let(:sha_bits) { '256' }
24
+ it_behaves_like '#signed'
25
+
26
+ describe 'changed key' do
27
+ let(:changed_public_key) { OpenSSL::PKey::RSA.generate(Rsa::KEY_BITS_MIN).public_key }
28
+ let(:data) { 'data' }
29
+ it 'fails #verified?' do
30
+ mac = Rsa.signed(sha_bits, private_key, data)
31
+ expect(Rsa.verified? mac, sha_bits, public_key, data).to be true
32
+ expect(Rsa.verified? mac, sha_bits, changed_public_key, data).to be false
33
+ end
34
+ end
35
+ end
36
+
37
+ describe 'RS384' do
38
+ let(:sha_bits) { '384' }
39
+ it_behaves_like '#signed'
40
+ end
41
+
42
+ describe 'RS512' do
43
+ let(:sha_bits) { '512' }
44
+ it_behaves_like '#signed'
45
+ end
46
+ end
47
+
48
+ context 'param validation' do
49
+ let(:data) { 'data' }
50
+ shared_examples_for 'invalid private_key' do
51
+ it 'raises' do
52
+ expect { Rsa.signed(sha_bits, private_key, data) }.to raise_error(RuntimeError, 'Invalid private key')
53
+ end
54
+ end
55
+
56
+ context 'private_key bit size (2047) < KEY_BITS_MIN (2048)' do
57
+ let(:private_key) { OpenSSL::PKey::RSA.generate(Rsa::KEY_BITS_MIN - 1) }
58
+ describe 'w 256 sha_bits' do
59
+ let(:sha_bits) { '256' }
60
+ it_behaves_like 'invalid private_key'
61
+ end
62
+
63
+ describe 'w 384 sha_bits' do
64
+ let(:sha_bits) { '384' }
65
+ it_behaves_like 'invalid private_key'
66
+ end
67
+
68
+ describe 'w 512 sha_bits' do
69
+ let(:sha_bits) { '512' }
70
+ it_behaves_like 'invalid private_key'
71
+ end
72
+ end
73
+
74
+ shared_examples_for '2048 bit private_key' do
75
+ it 'returns a 256-byte MAC string' do
76
+ mac = Rsa.signed(sha_bits, private_key, data)
77
+ expect(mac.bytesize).to eql 256
78
+ expect(mac.class).to eql String
79
+ end
80
+ end
81
+
82
+ context 'private_key bits (2048) == KEY_BITS_MIN (2048)' do
83
+ let(:private_key) { OpenSSL::PKey::RSA.generate(Rsa::KEY_BITS_MIN) }
84
+ describe 'w 256 sha_bits' do
85
+ let(:sha_bits) { '256' }
86
+ it_behaves_like '2048 bit private_key'
87
+ end
88
+
89
+ describe 'w 384 sha_bits' do
90
+ let(:sha_bits) { '384' }
91
+ it_behaves_like '2048 bit private_key'
92
+ end
93
+
94
+ describe 'w 512 sha_bits' do
95
+ let(:sha_bits) { '512' }
96
+ it_behaves_like '2048 bit private_key'
97
+ end
98
+ end
99
+
100
+ context 'blank private_key' do
101
+ let(:sha_bits) { '256' }
102
+ describe 'nil' do
103
+ let(:private_key) { nil }
104
+ it_behaves_like 'invalid private_key'
105
+ end
106
+
107
+ describe 'empty string' do
108
+ let(:private_key) { '' }
109
+ it 'raises' do
110
+ expect { Rsa.signed(sha_bits, private_key, data) }.to raise_error(NoMethodError)
111
+ end
112
+ end
113
+ end
114
+
115
+ describe 'w unrecognized sha_bits' do
116
+ let(:sha_bits) { '257' }
117
+ let(:private_key) { 'private_key' }
118
+ it 'raises' do
119
+ expect { Rsa.signed(sha_bits, private_key, data) }
120
+ .to raise_error(RuntimeError, 'Invalid sha_bits')
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -22,6 +22,13 @@ module JsonWebToken
22
22
  let(:verifying_key) { signing_key }
23
23
  it_behaves_like '#signed'
24
24
  end
25
+
26
+ describe 'RS256' do
27
+ let(:algorithm) { 'RS256' }
28
+ let(:signing_key) { OpenSSL::PKey::RSA.generate(2048) }
29
+ let(:verifying_key) { signing_key.public_key }
30
+ it_behaves_like '#signed'
31
+ end
25
32
  end
26
33
 
27
34
  context 'param validation' do
@@ -47,6 +54,16 @@ module JsonWebToken
47
54
  end
48
55
  end
49
56
  end
57
+
58
+ describe 'RS256' do
59
+ let(:private_key) { OpenSSL::PKey::RSA.generate(2048) }
60
+ let(:algorithm) { 'RS256' }
61
+ it 'returns a 256-byte MAC string' do
62
+ mac = Jwa.signed(algorithm, private_key, data)
63
+ expect(mac.bytesize).to eql 256
64
+ expect(mac.class).to eql String
65
+ end
66
+ end
50
67
  end
51
68
  end
52
69
  end
@@ -39,6 +39,18 @@ module JsonWebToken
39
39
  end
40
40
  end
41
41
  end
42
+
43
+ context 'w RS256 keys' do
44
+ let(:signing_key) { OpenSSL::PKey::RSA.generate(2048) }
45
+ let(:verifying_key) { signing_key.public_key }
46
+ context "w RS256 'alg' header parameter" do
47
+ let(:header) { {alg: 'RS256'} }
48
+ describe 'w passing a matching algorithm to #validate' do
49
+ let(:algorithm) { 'RS256' }
50
+ it_behaves_like 'w #validate'
51
+ end
52
+ end
53
+ end
42
54
  end
43
55
 
44
56
  context 'header validation' do
@@ -6,86 +6,112 @@ module JsonWebToken
6
6
  context '#create' do
7
7
  shared_examples_for 'w #validate' do
8
8
  it 'verified' do
9
- jwt = Jwt.create(claims, options)
10
- expect(Jwt.validate jwt, options).to include(claims)
9
+ jwt = Jwt.create(claims, create_options)
10
+ expect(Jwt.validate jwt, validate_options).to include(claims)
11
11
  end
12
12
  end
13
13
 
14
14
  shared_examples_for 'return message signature' do
15
15
  it 'plausible' do
16
- serialized_output = Jwt.create(claims, options)
17
- expect(plausible_message_signature? serialized_output).to be true
18
- end
19
- end
20
-
21
- shared_examples_for 'return unsecured jws' do
22
- it 'plausible' do
23
- serialized_output = Jwt.create(claims, options)
24
- expect(plausible_unsecured_jws? serialized_output).to be true
16
+ jwt = Jwt.create(claims, create_options)
17
+ expect(plausible_message_signature? jwt).to be true
25
18
  end
26
19
  end
27
20
 
28
21
  context 'w claims' do
29
22
  let(:claims) { {exp: 'tomorrow'} }
30
- context 'w key' do
31
- let(:key) { 'this_a_32_character_private_key!' }
23
+ context 'w HS256 keys' do
24
+ let(:signing_key) { 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C' }
25
+ let(:verifying_key) { signing_key }
26
+ let(:validate_options) { {key: verifying_key} }
32
27
  describe 'default header' do
33
- let(:options) { {key: key} }
28
+ let(:create_options) { {key: signing_key} }
34
29
  it_behaves_like 'w #validate'
35
30
  it_behaves_like 'return message signature'
36
31
  end
37
32
 
38
33
  describe 'passing header parameters' do
39
- let(:options) { {typ: 'JWT', alg: 'HS256', key: key} }
34
+ let(:create_options) { {typ: 'JWT', alg: 'HS256', key: signing_key} }
40
35
  it_behaves_like 'w #validate'
41
36
  it_behaves_like 'return message signature'
42
37
  end
43
38
 
44
- describe "w 'alg':'none' header parameter" do
45
- let(:options) { {typ: 'JWT', alg: 'none', key: key} }
46
- it_behaves_like 'w #validate'
47
- it_behaves_like 'return unsecured jws'
48
- end
49
-
50
39
  describe "w 'alg':'nil' header parameter" do
51
- let(:options) { {alg: nil, key: key} }
40
+ let(:create_options) { {alg: nil, key: signing_key} }
52
41
  it_behaves_like 'w #validate'
53
42
  it_behaves_like 'return message signature'
54
43
  end
55
44
 
56
45
  describe "w 'alg':'' header parameter" do
57
- let(:options) { {alg: nil, key: key} }
46
+ let(:create_options) { {alg: '', key: signing_key} }
58
47
  it_behaves_like 'w #validate'
59
48
  it_behaves_like 'return message signature'
60
49
  end
61
- end
62
50
 
63
- context 'w/o key' do
64
- let(:options) { {typ: 'JWT', alg: 'none'} }
65
51
  describe "w 'alg':'none' header parameter" do
52
+ let(:create_options) { {typ: 'JWT', alg: 'none', key: signing_key} }
53
+ it 'raises' do
54
+ jwt = Jwt.create(claims, create_options)
55
+ expect { Jwt.validate(jwt) }
56
+ .to raise_error(RuntimeError, "Algorithm not matching 'alg' header parameter")
57
+ end
58
+ end
59
+ end
60
+
61
+ context 'w RS256 keys' do
62
+ let(:signing_key) { OpenSSL::PKey::RSA.generate(2048) }
63
+ let(:verifying_key) { signing_key.public_key }
64
+ let(:validate_options) { {alg: 'RS256', key: verifying_key} }
65
+ describe 'passing header parameters' do
66
+ let(:create_options) { {typ: 'JWT', alg: 'RS256', key: signing_key} }
66
67
  it_behaves_like 'w #validate'
67
- it_behaves_like 'return unsecured jws'
68
+ it 'plausible' do
69
+ jwt = Jwt.create(claims, create_options)
70
+ expect(plausible_message_signature? jwt, 256).to be true
71
+ end
68
72
  end
69
73
  end
70
- end
71
74
 
72
- shared_examples_for 'claims not provided' do
73
- it 'raises' do
74
- expect { Jwt.create(claims, options) }
75
- .to raise_error(RuntimeError, 'Claims not provided')
75
+ context 'w/o key' do
76
+ context "w alg 'none' header parameter" do
77
+ let(:create_options) { {typ: 'JWT', alg: 'none'} }
78
+ describe "w validate alg 'none'" do
79
+ let(:validate_options) { {alg: 'none'} }
80
+ it 'validates a plausible unsecured jws' do
81
+ jwt = Jwt.create(claims, create_options)
82
+ expect(Jwt.validate jwt, validate_options).to include(claims)
83
+ expect(plausible_unsecured_jws? jwt).to be true
84
+ end
85
+ end
86
+
87
+ describe "w default validate alg" do
88
+ it 'raises' do
89
+ jwt = Jwt.create(claims, create_options)
90
+ expect { Jwt.validate(jwt) }
91
+ .to raise_error(RuntimeError, "Algorithm not matching 'alg' header parameter")
92
+ end
93
+ end
94
+ end
76
95
  end
77
96
  end
78
97
 
79
- context 'w secret' do
80
- let(:options) { {key: 'secret'} }
98
+ context 'param validation' do
99
+ let(:options) { {key: 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C'} }
100
+ shared_examples_for 'w/o claims' do
101
+ it 'raises' do
102
+ expect { Jwt.create(claims, options) }
103
+ .to raise_error(RuntimeError, 'Claims blank')
104
+ end
105
+ end
106
+
81
107
  describe 'w claims nil' do
82
108
  let(:claims) { nil }
83
- it_behaves_like 'claims not provided'
109
+ it_behaves_like 'w/o claims'
84
110
  end
85
111
 
86
112
  describe "w claims ''" do
87
113
  let(:claims) { '' }
88
- it_behaves_like 'claims not provided'
114
+ it_behaves_like 'w/o claims'
89
115
  end
90
116
  end
91
117
  end
@@ -35,27 +35,9 @@ describe JsonWebToken do
35
35
  end
36
36
  end
37
37
 
38
- context 'w/o key' do
39
- context "w create alg 'none'" do
40
- let(:create_options) { {alg: 'none'} }
41
- describe "w validate alg 'none'" do
42
- let(:validate_options) { {alg: 'none'} }
43
- it_behaves_like 'w #validate'
44
- end
45
-
46
- describe "w default validate alg" do
47
- it 'raises' do
48
- jwt = JsonWebToken.create(claims, create_options)
49
- expect { JsonWebToken.validate(jwt) }
50
- .to raise_error(RuntimeError, "Algorithm not matching 'alg' header parameter")
51
- end
52
- end
53
- end
54
-
55
- describe 'w default create alg' do
56
- it 'raises' do
57
- expect { JsonWebToken.create(claims) }.to raise_error(RuntimeError, 'Invalid key')
58
- end
38
+ describe 'w/o key w default header alg' do
39
+ it 'raises' do
40
+ expect { JsonWebToken.create(claims) }.to raise_error(RuntimeError, 'Invalid key')
59
41
  end
60
42
  end
61
43
  end
@@ -1,11 +1,15 @@
1
- def plausible_message_signature?(str)
2
- _parts_count(str) == 3
3
- end
1
+ require 'json_web_token/format/base64_url'
4
2
 
5
- def plausible_unsecured_jws?(str)
6
- _parts_count(str) == 2
3
+ include JsonWebToken::Format::Base64Url
4
+
5
+ def plausible_message_signature?(str, bytesize = 32)
6
+ parts = str.split('.')
7
+ return false unless parts.length == 3
8
+ mac = decode(parts[2])
9
+ mac.bytesize == bytesize && mac.class == String
7
10
  end
8
11
 
9
- def _parts_count(str)
10
- str.split('.').length
12
+ def plausible_unsecured_jws?(str)
13
+ return false unless str.end_with?('.')
14
+ str.split('.').length == 2
11
15
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json_web_token
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gary Fleshman
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-10 00:00:00.000000000 Z
11
+ date: 2015-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -30,20 +30,6 @@ dependencies:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.8.3
33
- - !ruby/object:Gem::Dependency
34
- name: pry-byebug
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '3.1'
40
- type: :development
41
- prerelease: false
42
- version_requirements: !ruby/object:Gem::Requirement
43
- requirements:
44
- - - "~>"
45
- - !ruby/object:Gem::Version
46
- version: '3.1'
47
33
  - !ruby/object:Gem::Dependency
48
34
  name: rspec
49
35
  requirement: !ruby/object:Gem::Requirement
@@ -59,19 +45,22 @@ dependencies:
59
45
  - !ruby/object:Gem::Version
60
46
  version: '3.3'
61
47
  description: Ruby implementation of the JSON Web Token Standard Track RFC 4627
62
- email: gf4cl@verizon.net
48
+ email: gfleshman@newforge-tech.com
63
49
  executables: []
64
50
  extensions: []
65
51
  extra_rdoc_files: []
66
52
  files:
67
53
  - ".gitignore"
68
54
  - ".rspec"
55
+ - CHANGELOG.md
69
56
  - Gemfile
70
57
  - LICENSE
71
58
  - README.md
72
59
  - json_web_token.gemspec
73
60
  - lib/json_web_token.rb
61
+ - lib/json_web_token/algorithm/common.rb
74
62
  - lib/json_web_token/algorithm/hmac.rb
63
+ - lib/json_web_token/algorithm/rsa.rb
75
64
  - lib/json_web_token/format/base64_url.rb
76
65
  - lib/json_web_token/jwa.rb
77
66
  - lib/json_web_token/jws.rb
@@ -79,6 +68,7 @@ files:
79
68
  - lib/json_web_token/util.rb
80
69
  - lib/json_web_token/version.rb
81
70
  - spec/json_web_token/algorithm/hmac_spec.rb
71
+ - spec/json_web_token/algorithm/rsa_spec.rb
82
72
  - spec/json_web_token/format/base64_url_spec.rb
83
73
  - spec/json_web_token/jwa_spec.rb
84
74
  - spec/json_web_token/jws_spec.rb
@@ -99,7 +89,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
99
89
  requirements:
100
90
  - - ">="
101
91
  - !ruby/object:Gem::Version
102
- version: '0'
92
+ version: 2.0.0
103
93
  required_rubygems_version: !ruby/object:Gem::Requirement
104
94
  requirements:
105
95
  - - ">="