json_web_token 0.1.2 → 0.2.0

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: 973c4dc365edec01a991491309d0332b3898dd57
4
- data.tar.gz: 05d71d73011230c2b2f074eed6d2ea86bf0be1cf
3
+ metadata.gz: a1bab6670404e2a3185dabc1944f130891cb6de6
4
+ data.tar.gz: 142fb6257f61b6c906e29519af046289ba085508
5
5
  SHA512:
6
- metadata.gz: 640c8738d52377295275bc4c5498b02a7fd427ffdf2b8d14b5765acc90fd8868c401799f0ccbb0535d37143ee90138ff5f80621edee6c902344106a5f1727786
7
- data.tar.gz: fa2e818be12da6b351eb202b4ca2de9ab0efdd48f789d1e61e2e3683ae7fa9f50026e716d69578eb2c850266f72b5cc6bac4c94d570148cd4194d06ceb67b49f
6
+ metadata.gz: 263eb8d5f5855937117026134f7dce4f76c37c48a3c0ef955d4c21d47e41395793cc3c55bea20de613f56c1808cc6ae55af919d16bfae214a81b1a8ec9f1d840
7
+ data.tar.gz: 29073ba554234ce1581d0608e29e17e12885c962ecbda6f402564c7bc85aa18669c9a47039bd817ce09e42e6def463623fd97f554bccff2b76049f610af85bd5
@@ -1,5 +1,10 @@
1
1
  ## Changelog
2
2
 
3
+ ### v0.2.0 (2015-08-02)
4
+
5
+ * backward incompatible changes
6
+ * Top level API now #sign and #verify
7
+
3
8
  ### v0.1.2 (2015-08-02)
4
9
 
5
10
  * enhancements
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # JSON Web Token [![travis][ci_img]][travis] [![yard docs][yd_img]][yard_docs] [![code climate][cc_img]][code_climate]
2
2
 
3
- ## A JSON Web Token implementation for Ruby
3
+ ## A JSON Web Token (JWT) implementation for Ruby
4
4
 
5
5
  ### Description
6
- A Ruby implementation of the JSON Web Token (JWT) Standards Track [RFC 7519][rfc7519]
6
+ A Ruby implementation of the JSON Web Token standard [RFC 7519][rfc7519]
7
7
 
8
8
  ## Installation
9
9
  gem install json_web_token
@@ -30,7 +30,7 @@ Secure Cross-Origin Resource Sharing ([CORS][cors]) using the [rack-cors][rack-c
30
30
 
31
31
  ## Usage
32
32
 
33
- ### JsonWebToken.create(claims, options)
33
+ ### JsonWebToken.sign(claims, options)
34
34
 
35
35
  Returns a JSON Web Token string
36
36
 
@@ -47,7 +47,7 @@ Example
47
47
  require 'json_web_token'
48
48
 
49
49
  # sign with default algorithm, HMAC SHA256
50
- jwt = JsonWebToken.create({foo: 'bar'}, key: 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C')
50
+ jwt = JsonWebToken.sign({foo: 'bar'}, key: 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C')
51
51
 
52
52
  # sign with RSA SHA256 algorithm
53
53
  opts = {
@@ -55,14 +55,14 @@ opts = {
55
55
  key: < RSA private key >
56
56
  }
57
57
 
58
- jwt = JsonWebToken.create({foo: 'bar'}, opts)
58
+ jwt = JsonWebToken.sign({foo: 'bar'}, opts)
59
59
 
60
60
  # unsecured token (algorithm is 'none')
61
- jwt = JsonWebToken.create({foo: 'bar'}, alg: 'none')
61
+ jwt = JsonWebToken.sign({foo: 'bar'}, alg: 'none')
62
62
 
63
63
  ```
64
64
 
65
- ### JsonWebToken.validate(jwt, options)
65
+ ### JsonWebToken.verify(jwt, options)
66
66
 
67
67
  Returns either:
68
68
  * a JWT claims set string or hash, if the Message Authentication Code (MAC), or signature, is verified
@@ -83,7 +83,7 @@ require 'json_web_token'
83
83
  secure_jwt_example = 'eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt.cGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'
84
84
 
85
85
  # verify with default algorithm, HMAC SHA256
86
- claims = JsonWebToken.validate(secure_jwt_example, key: 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C')
86
+ claims = JsonWebToken.verify(secure_jwt_example, key: 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C')
87
87
 
88
88
  # verify with RSA SHA256 algorithm
89
89
  opts = {
@@ -91,12 +91,12 @@ opts = {
91
91
  key: < RSA public key >
92
92
  }
93
93
 
94
- claims = JsonWebToken.validate(jwt, opts)
94
+ claims = JsonWebToken.verify(jwt, opts)
95
95
 
96
96
  # unsecured token (algorithm is 'none')
97
97
  unsecured_jwt_example = 'eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt.'
98
98
 
99
- claims = JsonWebToken.validate(unsecured_jwt_example, alg: 'none')
99
+ claims = JsonWebToken.verify(unsecured_jwt_example, alg: 'none')
100
100
 
101
101
  ```
102
102
  ### Supported encryption algorithms
@@ -115,7 +115,7 @@ ES512 | ECDSA using P-521 and SHA-512
115
115
  none | No digital signature or MAC performed (unsecured)
116
116
 
117
117
  ### Supported Ruby Versions
118
- Ruby 2.0 and up
118
+ Ruby 2.0.0 and up
119
119
 
120
120
  ### Limitations
121
121
  Future implementation may include these features:
@@ -141,7 +141,7 @@ Future implementation may include these features:
141
141
 
142
142
  [travis]: https://travis-ci.org/garyf/json_web_token
143
143
  [ci_img]: https://travis-ci.org/garyf/json_web_token.svg?branch=master
144
- [yard_docs]: http://www.rubydoc.info/gems/json_web_token
144
+ [yard_docs]: http://www.rubydoc.info/github/garyf/json_web_token
145
145
  [yd_img]: http://img.shields.io/badge/yard-docs-blue.svg
146
146
  [code_climate]: https://codeclimate.com/github/garyf/json_web_token
147
147
  [cc_img]: https://codeclimate.com/github/garyf/json_web_token/badges/gpa.svg
@@ -16,6 +16,6 @@ Gem::Specification.new do |s|
16
16
  # optional
17
17
  s.add_runtime_dependency 'json', '~> 1.8', '>= 1.8.3'
18
18
  s.add_development_dependency 'rspec', '~> 3.3'
19
- s.description = 'Ruby implementation of the JSON Web Token standard, RFC 7519'
19
+ s.description = 'Ruby implementation of the JSON Web Token (JWT) standard, RFC 7519'
20
20
  s.required_ruby_version = '>= 2.0.0'
21
21
  end
@@ -1,14 +1,35 @@
1
1
  require 'json_web_token/jwt'
2
2
 
3
+ # Top level interface, or API, to sign and verify a JSON Web Token (JWT)
4
+ # @see http://tools.ietf.org/html/rfc7519
3
5
  module JsonWebToken
4
6
 
5
7
  module_function
6
8
 
7
- def create(claims, options = {})
9
+ # @param claims [Hash] a collection of name/value pairs asserting information about a subject
10
+ # @param options [Hash] specify the desired signing algorithm and signing key
11
+ # @return [String] a JSON Web Token, representing digitally signed claims
12
+ # @example
13
+ # claims = {iss: 'joe', exp: 1300819380, :'http://example.com/is_root' => true}
14
+ # options = {alg: 'HS256', key: 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C'}
15
+ # JsonWebToken.sign(claims, options)
16
+ # # => 'eyJhbGciOiJIUzI1NiJ9.cGF5bG9hZA.uVTaOdyzp_f4mT_hfzU8LnCzdmlVC4t2itHDEYUZym4'
17
+ def sign(claims, options)
8
18
  Jwt.sign(claims, options)
9
19
  end
10
-
11
- def validate(jwt, options = {})
20
+
21
+ # @param jwt [String] a JSON Web Token
22
+ # @param options [Hash] specify the desired verifying algorithm and verifying key
23
+ # @return [Hash] a JWT claims set if the jwt verifies, or +error: 'Invalid'+ otherwise
24
+ # @example
25
+ # jwt = 'eyJhbGciOiJIUzI1NiJ9.cGF5bG9hZA.uVTaOdyzp_f4mT_hfzU8LnCzdmlVC4t2itHDEYUZym4'
26
+ # options = {alg: 'HS256', key: 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C'}
27
+ # JsonWebToken.verify(jwt, options)
28
+ # # => {iss: 'joe', exp: 1300819380, :'http://example.com/is_root' => true}
29
+ def verify(jwt, options)
12
30
  Jwt.verify(jwt, options)
13
31
  end
14
32
  end
33
+
34
+ # alias
35
+ JWT = JsonWebToken
@@ -2,6 +2,12 @@ require 'openssl'
2
2
 
3
3
  module JsonWebToken
4
4
  module Format
5
+ # ASN1 data structures are usually encoded using the Distinguished Encoding Rules (DER).
6
+ # The ASN1 module provides the necessary classes that allow generation of ASN1 data
7
+ # structures and the methods to encode them using a DER encoding. The decode method allows
8
+ # parsing arbitrary DER-encoded data to a Ruby object that can then be modified and
9
+ # re-encoded at will.
10
+ # @see http://docs.ruby-lang.org/en/2.1.0/OpenSSL/ASN1.html
5
11
  module Asn1
6
12
 
7
13
  KEY_BITS = {
@@ -12,12 +18,6 @@ module JsonWebToken
12
18
 
13
19
  module_function
14
20
 
15
- # ASN1 data structures are usually encoded using the Distinguished Encoding Rules (DER).
16
- # The ASN1 module provides the necessary classes that allow generation of ASN1 data
17
- # structures and the methods to encode them using a DER encoding. The decode method allows
18
- # parsing arbitrary DER-encoded data to a Ruby object that can then be modified and
19
- # re-encoded at will. (see http://docs.ruby-lang.org/en/2.1.0/OpenSSL/ASN1.html)
20
-
21
21
  def der_to_signature(der, sha_bits)
22
22
  signature_pair = OpenSSL::ASN1.decode(der).value
23
23
  width = per_part_byte_count(sha_bits)
@@ -33,8 +33,6 @@ module JsonWebToken
33
33
  asn1_seq.to_der
34
34
  end
35
35
 
36
- # private
37
-
38
36
  def per_part_byte_count(sha_bits)
39
37
  bits = KEY_BITS[sha_bits]
40
38
  bits ? (bits + 7) / 8 : fail('Invalid sha_bits')
@@ -4,6 +4,7 @@ module JsonWebToken
4
4
  # Encode claims for transmission as a JSON object that is used as the payload of a JSON Web
5
5
  # Signature (JWS) structure, enabling the claims to be integrity protected with a Message
6
6
  # Authentication Code (MAC), to be later verified
7
+ # @see http://tools.ietf.org/html/rfc7519
7
8
  module Jwt
8
9
 
9
10
  ALG_DEFAULT = 'HS256'
@@ -19,12 +20,12 @@ module JsonWebToken
19
20
  # (e.g String for Hmac | OpenSSL::PKey::RSA | OpenSSL::PKey::EC)
20
21
  # @return [String] a JSON Web Token, representing digitally signed claims
21
22
  # @example
22
- # claims = {iss: 'joe', exp: 1300819380, 'http://example.com/is_root' => true}
23
+ # claims = {iss: 'joe', exp: 1300819380, :'http://example.com/is_root' => true}
23
24
  # options = {alg: 'HS256', key: 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C'}
24
25
  # Jwt.sign(claims, options)
25
26
  # # => 'eyJhbGciOiJIUzI1NiJ9.cGF5bG9hZA.uVTaOdyzp_f4mT_hfzU8LnCzdmlVC4t2itHDEYUZym4'
26
27
  # @see http://tools.ietf.org/html/rfc7519#section-7.1
27
- def sign(claims, options = {})
28
+ def sign(claims, options)
28
29
  message = validated_message(claims)
29
30
  header = config_header(options)
30
31
  return Jws.unsecured_message(header, message) if header[:alg] == 'none'
@@ -33,14 +34,14 @@ module JsonWebToken
33
34
 
34
35
  # @param jwt [String] a JSON Web Token
35
36
  # @param options [Hash] specify the desired verifying algorithm and verifying key
36
- # @return [Hash] a JWT claims set if the jwt verifies, or +{error: 'Invalid'}+ otherwise
37
+ # @return [Hash] a JWT claims set if the jwt verifies, or +error: 'Invalid'+ otherwise
37
38
  # @example
38
39
  # jwt = 'eyJhbGciOiJIUzI1NiJ9.cGF5bG9hZA.uVTaOdyzp_f4mT_hfzU8LnCzdmlVC4t2itHDEYUZym4'
39
40
  # options = {alg: 'HS256', key: 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C'}
40
41
  # Jwt.verify(jwt, options)
41
- # # => {iss: 'joe', exp: 1300819380, 'http://example.com/is_root' => true}
42
+ # # => {iss: 'joe', exp: 1300819380, :'http://example.com/is_root' => true}
42
43
  # @see see http://tools.ietf.org/html/rfc7519#section-7.2
43
- def verify(jwt, options = {})
44
+ def verify(jwt, options)
44
45
  alg = options[:alg] || ALG_DEFAULT
45
46
  jws = Jws.verify(jwt, alg, options[:key])
46
47
  jws ? Util.symbolize_keys(decoded_message_json_to_hash jws) : {error: 'invalid'}
@@ -1,22 +1,34 @@
1
1
  module JsonWebToken
2
+ # Utility methods
2
3
  module Util
3
4
 
4
5
  module_function
5
6
 
6
- # https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-3.2
7
+ # @param a [String]
8
+ # @param b [String]
9
+ # @return [Boolean] a predicate that compares two strings for equality in constant-time
10
+ # to avoid timing attacks
11
+ # @example
12
+ # Util.constant_time_compare?("a", "A")
13
+ # # => false
14
+ # @see https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-3.2
15
+ # @see cf. rails activesupport/lib/active_support/security_utils.rb
7
16
  def constant_time_compare?(a, b)
8
17
  return false if a.nil? || b.nil? || a.empty? || b.empty?
9
18
  secure_compare(a, b)
10
19
  end
11
20
 
12
- # cf. rails activesupport/lib/active_support/core_ext/hash/keys.rb
21
+ # @param hsh [Hash]
22
+ # @return [Hash] a new hash with all keys converted to symbols,
23
+ # as long as they respond to to_sym
24
+ # @example
25
+ # Util.symbolize_keys({'a' => 0, 'b' => '2', c: '3'})
26
+ # # => {a: 0, b: '2', c: '3'}
27
+ # @see cf. rails activesupport/lib/active_support/core_ext/hash/keys.rb
13
28
  def symbolize_keys(hsh)
14
29
  transform_keys(hsh) { |key| key.to_sym rescue key }
15
30
  end
16
31
 
17
- # private
18
-
19
- # cf. rails activesupport/lib/active_support/security_utils.rb
20
32
  def secure_compare(a, b)
21
33
  return false unless a.bytesize == b.bytesize
22
34
  l = a.unpack "C#{a.bytesize}"
@@ -1,3 +1,3 @@
1
1
  module JsonWebToken
2
- VERSION = '0.1.2'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -20,7 +20,7 @@ module JsonWebToken
20
20
  end
21
21
 
22
22
  context 'w claims' do
23
- let(:claims) { { iss: 'joe', exp: 1300819380, 'http://example.com/is_root': true} }
23
+ let(:claims) { { iss: 'joe', exp: 1300819380, :'http://example.com/is_root' => true} }
24
24
  context 'w HS256 keys' do
25
25
  let(:signing_key) { 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C' }
26
26
  let(:verifying_key) { signing_key }
@@ -118,7 +118,7 @@ module JsonWebToken
118
118
  describe 'w default verify alg' do
119
119
  it 'raises' do
120
120
  jwt = Jwt.sign(claims, sign_options)
121
- expect { Jwt.verify(jwt) }
121
+ expect { Jwt.verify(jwt, {alg: nil}) }
122
122
  .to raise_error(RuntimeError, "Algorithm not matching 'alg' header parameter")
123
123
  end
124
124
  end
@@ -1,12 +1,12 @@
1
1
  require 'json_web_token'
2
2
 
3
3
  describe JsonWebToken do
4
- context '#create' do
5
- let(:claims) { {exp: 'tomorrow'} }
6
- shared_examples_for 'w #validate' do
7
- it 'is verified' do
8
- jwt = JsonWebToken.create(claims, create_options)
9
- expect(JsonWebToken.validate jwt, validate_options).to include(claims)
4
+ context '#sign' do
5
+ let(:claims) { { iss: 'joe', exp: 1300819380, :'http://example.com/is_root' => true} }
6
+ shared_examples_for 'w #verify' do
7
+ it 'w a claims set' do
8
+ jwt = JsonWebToken.sign(claims, sign_options)
9
+ expect(JsonWebToken.verify jwt, verify_options).to include(claims)
10
10
  end
11
11
  end
12
12
 
@@ -15,29 +15,38 @@ describe JsonWebToken do
15
15
  let(:verifying_key) { signing_key }
16
16
 
17
17
  describe 'default alg' do
18
- let(:create_options) { {key: signing_key} }
19
- let(:validate_options) { {key: verifying_key} }
20
- it_behaves_like 'w #validate'
18
+ let(:sign_options) { {key: signing_key} }
19
+ let(:verify_options) { {key: verifying_key} }
20
+ it_behaves_like 'w #verify'
21
21
  end
22
22
 
23
23
  context "w 'alg' option" do
24
24
  describe 'HS256' do
25
- let(:create_options) { {alg: 'HS256', key: signing_key} }
26
- let(:validate_options) { {alg: 'HS256', key: verifying_key} }
27
- it_behaves_like 'w #validate'
25
+ let(:sign_options) { {alg: 'HS256', key: signing_key} }
26
+ let(:verify_options) { {alg: 'HS256', key: verifying_key} }
27
+ it_behaves_like 'w #verify'
28
28
  end
29
29
 
30
30
  describe "w alg 'none'" do
31
- let(:create_options) { {alg: 'none', key: signing_key} }
32
- let(:validate_options) { {alg: 'none', key: verifying_key} }
33
- it_behaves_like 'w #validate'
31
+ let(:sign_options) { {alg: 'none', key: signing_key} }
32
+ let(:verify_options) { {alg: 'none', key: verifying_key} }
33
+ it_behaves_like 'w #verify'
34
34
  end
35
35
  end
36
36
  end
37
37
 
38
38
  describe 'w/o key w default header alg' do
39
39
  it 'raises' do
40
- expect { JsonWebToken.create(claims) }.to raise_error(RuntimeError, 'Invalid shared key')
40
+ expect { JsonWebToken.sign(claims, {}) }.to raise_error(RuntimeError, 'Invalid shared key')
41
+ end
42
+ end
43
+ end
44
+
45
+ context 'module alias JWT' do
46
+ describe '#sign' do
47
+ let(:claims) { { iss: 'joe', exp: 1300819380, :'http://example.com/is_root' => true} }
48
+ it 'recognized' do
49
+ expect(JsonWebToken.sign(claims, key: 'gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr9C')).to be
41
50
  end
42
51
  end
43
52
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json_web_token
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gary Fleshman
@@ -14,45 +14,45 @@ dependencies:
14
14
  name: json
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
19
  version: '1.8'
20
- - - ">="
20
+ - - '>='
21
21
  - !ruby/object:Gem::Version
22
22
  version: 1.8.3
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
- - - "~>"
27
+ - - ~>
28
28
  - !ruby/object:Gem::Version
29
29
  version: '1.8'
30
- - - ">="
30
+ - - '>='
31
31
  - !ruby/object:Gem::Version
32
32
  version: 1.8.3
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: rspec
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - "~>"
37
+ - - ~>
38
38
  - !ruby/object:Gem::Version
39
39
  version: '3.3'
40
40
  type: :development
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - "~>"
44
+ - - ~>
45
45
  - !ruby/object:Gem::Version
46
46
  version: '3.3'
47
- description: Ruby implementation of the JSON Web Token standard, RFC 7519
47
+ description: Ruby implementation of the JSON Web Token (JWT) standard, RFC 7519
48
48
  email: gfleshman@newforge-tech.com
49
49
  executables: []
50
50
  extensions: []
51
51
  extra_rdoc_files: []
52
52
  files:
53
- - ".gitignore"
54
- - ".rspec"
55
- - ".travis.yml"
53
+ - .gitignore
54
+ - .rspec
55
+ - .travis.yml
56
56
  - CHANGELOG.md
57
57
  - Gemfile
58
58
  - LICENSE
@@ -93,12 +93,12 @@ require_paths:
93
93
  - lib
94
94
  required_ruby_version: !ruby/object:Gem::Requirement
95
95
  requirements:
96
- - - ">="
96
+ - - '>='
97
97
  - !ruby/object:Gem::Version
98
98
  version: 2.0.0
99
99
  required_rubygems_version: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - '>='
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  requirements: []