jwt 0.1.8 → 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
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