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.
- data/Rakefile +2 -1
- data/jwt.gemspec +4 -3
- data/lib/jwt.rb +32 -25
- data/spec/jwt_spec.rb +21 -1
- 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.
|
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
|
data/jwt.gemspec
CHANGED
@@ -2,17 +2,18 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "jwt"
|
5
|
-
s.version = "0.1.
|
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 = "
|
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.
|
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
|
-
|
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
|
25
|
-
private_key.sign(OpenSSL::Digest
|
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
|
29
|
-
public_key.verify(OpenSSL::Digest
|
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
|
33
|
-
OpenSSL::HMAC.digest(OpenSSL::Digest
|
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
|
37
|
-
str +=
|
38
|
-
Base64.decode64(str.
|
38
|
+
def base64url_decode(str)
|
39
|
+
str += "=" * (4 - str.length.modulo(4))
|
40
|
+
Base64.decode64(str.tr("-_", "+/"))
|
39
41
|
end
|
40
42
|
|
41
|
-
def
|
42
|
-
Base64.encode64(str).
|
43
|
+
def base64url_encode(str)
|
44
|
+
Base64.encode64(str).tr("-_", "+/").gsub(/[\n=]/, "")
|
43
45
|
end
|
44
46
|
|
45
|
-
def
|
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
|
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
|
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
|
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[
|
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
|
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
|
|
data/spec/jwt_spec.rb
CHANGED
@@ -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).
|
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.
|
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:
|
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
|