json_web_token 0.0.1 → 0.0.2

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.
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
  - - ">="