jwt 0.1.8 → 0.1.10

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.
Files changed (5) hide show
  1. data/Rakefile +2 -1
  2. data/jwt.gemspec +4 -3
  3. data/lib/jwt.rb +32 -25
  4. data/spec/jwt_spec.rb +21 -1
  5. metadata +4 -4
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
  require 'rake'
3
3
  require 'echoe'
4
4
 
5
- Echoe.new('jwt', '0.1.8') do |p|
5
+ Echoe.new('jwt', '0.1.10') do |p|
6
6
  p.description = "JSON Web Token implementation in Ruby"
7
7
  p.url = "http://github.com/progrium/ruby-jwt"
8
8
  p.author = "Jeff Lindsay"
@@ -10,6 +10,7 @@ Echoe.new('jwt', '0.1.8') do |p|
10
10
  p.ignore_pattern = ["tmp/*"]
11
11
  p.runtime_dependencies = ["multi_json >=1.5"]
12
12
  p.development_dependencies = ["echoe >=4.6.3"]
13
+ p.licenses = "MIT"
13
14
  end
14
15
 
15
16
  task :test do
@@ -2,17 +2,18 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "jwt"
5
- s.version = "0.1.8"
5
+ s.version = "0.1.10"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Jeff Lindsay"]
9
- s.date = "2013-03-14"
9
+ s.date = "2014-01-10"
10
10
  s.description = "JSON Web Token implementation in Ruby"
11
11
  s.email = "progrium@gmail.com"
12
12
  s.extra_rdoc_files = ["lib/jwt.rb"]
13
13
  s.files = ["Rakefile", "lib/jwt.rb", "spec/helper.rb", "spec/jwt_spec.rb", "Manifest", "jwt.gemspec"]
14
14
  s.homepage = "http://github.com/progrium/ruby-jwt"
15
- s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Jwt", "--main", "README.md"]
15
+ s.licenses = ["MIT"]
16
+ s.rdoc_options = ["--line-numbers", "--title", "Jwt", "--main", "README.md"]
16
17
  s.require_paths = ["lib"]
17
18
  s.rubyforge_project = "jwt"
18
19
  s.rubygems_version = "1.8.23"
data/lib/jwt.rb CHANGED
@@ -11,7 +11,9 @@ require "multi_json"
11
11
  module JWT
12
12
  class DecodeError < StandardError; end
13
13
 
14
- def self.sign(algorithm, msg, key)
14
+ module_function
15
+
16
+ def sign(algorithm, msg, key)
15
17
  if ["HS256", "HS384", "HS512"].include?(algorithm)
16
18
  sign_hmac(algorithm, msg, key)
17
19
  elsif ["RS256", "RS384", "RS512"].include?(algorithm)
@@ -21,57 +23,60 @@ module JWT
21
23
  end
22
24
  end
23
25
 
24
- def self.sign_rsa(algorithm, msg, private_key)
25
- private_key.sign(OpenSSL::Digest::Digest.new(algorithm.sub('RS', 'sha')), msg)
26
+ def sign_rsa(algorithm, msg, private_key)
27
+ private_key.sign(OpenSSL::Digest.new(algorithm.sub("RS", "sha")), msg)
26
28
  end
27
29
 
28
- def self.verify_rsa(algorithm, public_key, signing_input, signature)
29
- public_key.verify(OpenSSL::Digest::Digest.new(algorithm.sub('RS', 'sha')), signature, signing_input)
30
+ def verify_rsa(algorithm, public_key, signing_input, signature)
31
+ public_key.verify(OpenSSL::Digest.new(algorithm.sub("RS", "sha")), signature, signing_input)
30
32
  end
31
33
 
32
- def self.sign_hmac(algorithm, msg, key)
33
- OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new(algorithm.sub('HS', 'sha')), key, msg)
34
+ def sign_hmac(algorithm, msg, key)
35
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub("HS", "sha")), key, msg)
34
36
  end
35
37
 
36
- def self.base64url_decode(str)
37
- str += '=' * (4 - str.length.modulo(4))
38
- Base64.decode64(str.gsub("-", "+").gsub("_", "/"))
38
+ def base64url_decode(str)
39
+ str += "=" * (4 - str.length.modulo(4))
40
+ Base64.decode64(str.tr("-_", "+/"))
39
41
  end
40
42
 
41
- def self.base64url_encode(str)
42
- Base64.encode64(str).gsub("+", "-").gsub("/", "_").gsub("\n", "").gsub('=', '')
43
+ def base64url_encode(str)
44
+ Base64.encode64(str).tr("-_", "+/").gsub(/[\n=]/, "")
43
45
  end
44
46
 
45
- def self.encode(payload, key, algorithm='HS256', header_fields={})
47
+ def encode(payload, key, algorithm="HS256", header_fields={})
46
48
  algorithm ||= "none"
47
49
  segments = []
48
50
  header = {"typ" => "JWT", "alg" => algorithm}.merge(header_fields)
49
51
  segments << base64url_encode(MultiJson.encode(header))
50
52
  segments << base64url_encode(MultiJson.encode(payload))
51
- signing_input = segments.join('.')
52
- if algorithm != "none"
53
+ signing_input = segments.join(".")
54
+ if algorithm == "none"
55
+ segments << ""
56
+ else
53
57
  signature = sign(algorithm, signing_input, key)
54
58
  segments << base64url_encode(signature)
55
- else
56
- segments << ""
57
59
  end
58
- segments.join('.')
60
+ segments.join(".")
59
61
  end
60
62
 
61
- def self.decode(jwt, key=nil, verify=true, &keyfinder)
62
- segments = jwt.split('.')
63
+ def decode(jwt, key=nil, verify=true, &keyfinder)
64
+ segments = jwt.split(".")
63
65
  raise JWT::DecodeError.new("Not enough or too many segments") unless [2,3].include? segments.length
64
66
  header_segment, payload_segment, crypto_segment = segments
65
- signing_input = [header_segment, payload_segment].join('.')
67
+ signing_input = [header_segment, payload_segment].join(".")
66
68
  begin
67
69
  header = MultiJson.decode(base64url_decode(header_segment))
68
70
  payload = MultiJson.decode(base64url_decode(payload_segment))
69
- signature = base64url_decode(crypto_segment) if verify
70
- rescue MultiJson::LoadError => e
71
+ signature = base64url_decode(crypto_segment.to_s) if verify
72
+ rescue MultiJson::LoadError
71
73
  raise JWT::DecodeError.new("Invalid segment encoding")
72
74
  end
75
+
76
+ raise JWT::DecodeError.new("Not enough or too many segments") unless header && payload
77
+
73
78
  if verify
74
- algo = header['alg']
79
+ algo = header["alg"]
75
80
 
76
81
  if keyfinder
77
82
  key = keyfinder.call(header)
@@ -87,6 +92,8 @@ module JWT
87
92
  end
88
93
  rescue OpenSSL::PKey::PKeyError
89
94
  raise JWT::DecodeError.new("Signature verification failed")
95
+ ensure
96
+ OpenSSL.errors.clear
90
97
  end
91
98
  end
92
99
  payload
@@ -94,7 +101,7 @@ module JWT
94
101
 
95
102
  # From devise
96
103
  # constant-time comparison algorithm to prevent timing attacks
97
- def self.secure_compare(a, b)
104
+ def secure_compare(a, b)
98
105
  return false if a.nil? || b.nil? || a.empty? || b.empty? || a.bytesize != b.bytesize
99
106
  l = a.unpack "C#{a.bytesize}"
100
107
 
@@ -58,6 +58,21 @@ describe JWT do
58
58
  lambda { JWT.decode(jwt, bad_private_key.public_key) }.should raise_error(JWT::DecodeError)
59
59
  end
60
60
 
61
+ it "raises exception with invalid signature" do
62
+ example_payload = {"hello" => "world"}
63
+ example_secret = 'secret'
64
+ example_jwt = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJoZWxsbyI6ICJ3b3JsZCJ9.'
65
+ lambda { JWT.decode(example_jwt, example_secret) }.should raise_error(JWT::DecodeError)
66
+ end
67
+
68
+ it "raises exception with nonexistent header" do
69
+ lambda { JWT.decode("..stuff") }.should raise_error(JWT::DecodeError)
70
+ end
71
+
72
+ it "raises exception with nonexistent payload" do
73
+ lambda { JWT.decode("eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9..stuff") }.should raise_error(JWT::DecodeError)
74
+ end
75
+
61
76
  it "allows decoding without key" do
62
77
  right_secret = 'foo'
63
78
  bad_secret = 'bar'
@@ -93,7 +108,7 @@ describe JWT do
93
108
  signature = JWT.base64url_decode(crypto_segment)
94
109
  signature.should_not_receive('==')
95
110
  JWT.should_receive(:base64url_decode).with(crypto_segment).once.and_return(signature)
96
- JWT.should_receive(:base64url_decode).any_number_of_times.and_call_original
111
+ JWT.should_receive(:base64url_decode).at_least(:once).and_call_original
97
112
 
98
113
  JWT.decode(jwt, secret)
99
114
  end
@@ -115,6 +130,11 @@ describe JWT do
115
130
  end
116
131
  end
117
132
 
133
+ # no method should leave OpenSSL.errors populated
134
+ after do
135
+ OpenSSL.errors.should be_empty
136
+ end
137
+
118
138
  it "raise exception on invalid signature" do
119
139
  pubkey = OpenSSL::PKey::RSA.new(<<-PUBKEY)
120
140
  -----BEGIN PUBLIC KEY-----
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jwt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.10
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-03-14 00:00:00.000000000 Z
12
+ date: 2014-01-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
@@ -57,11 +57,11 @@ files:
57
57
  - Manifest
58
58
  - jwt.gemspec
59
59
  homepage: http://github.com/progrium/ruby-jwt
60
- licenses: []
60
+ licenses:
61
+ - MIT
61
62
  post_install_message:
62
63
  rdoc_options:
63
64
  - --line-numbers
64
- - --inline-source
65
65
  - --title
66
66
  - Jwt
67
67
  - --main