sandal 0.5.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +10 -1
- data/lib/sandal.rb +11 -3
- data/lib/sandal/version.rb +1 -1
- data/sandal.gemspec +1 -1
- data/spec/sandal_spec.rb +85 -22
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ff4b80562db5433640998a394546147889b724e
|
4
|
+
data.tar.gz: 57ab4cfaf1b8145f179bebc192142428cc2d8b4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 152b2ee352ff3d3ae11a4cc301e7f61b39f9b668678dd2049b0c3246e57b2633b9cbb0adc62d82cd4a920befd8d2b89fae13b5104e26b2a2e2edf9b216599ca2
|
7
|
+
data.tar.gz: c57f26aefdecb14a9d9966b21ca1e3fdf8016b9cc0b201f432a59f90a8f3030d87fac71cac3530fe2d449ed316286c21cbac37ddf946c18cbe11736bdc26182b
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Sandal [![Build Status](https://travis-ci.org/gregbeech/sandal.png?branch=master)](https://travis-ci.org/gregbeech/sandal) [![Coverage Status](https://coveralls.io/repos/gregbeech/sandal/badge.png?branch=master)](https://coveralls.io/r/gregbeech/sandal) [![Code Climate](https://codeclimate.com/github/gregbeech/sandal.png)](https://codeclimate.com/github/gregbeech/sandal) [![Dependency Status](https://gemnasium.com/gregbeech/sandal.png)](https://gemnasium.com/gregbeech/sandal)
|
1
|
+
# Sandal [![Gem Version](https://badge.fury.io/rb/sandal.png)](http://badge.fury.io/rb/sandal) [![Build Status](https://travis-ci.org/gregbeech/sandal.png?branch=master)](https://travis-ci.org/gregbeech/sandal) [![Coverage Status](https://coveralls.io/repos/gregbeech/sandal/badge.png?branch=master)](https://coveralls.io/r/gregbeech/sandal) [![Code Climate](https://codeclimate.com/github/gregbeech/sandal.png)](https://codeclimate.com/github/gregbeech/sandal) [![Dependency Status](https://gemnasium.com/gregbeech/sandal.png)](https://gemnasium.com/gregbeech/sandal)
|
2
2
|
|
3
3
|
A Ruby library for creating and reading [JSON Web Tokens (JWT) draft-13](http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-13), supporting [JSON Web Signatures (JWS) draft-18](http://tools.ietf.org/html/draft-ietf-jose-json-web-signature-18) and [JSON Web Encryption (JWE) draft-18](http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-18). See the [CHANGELOG](CHANGELOG.md) for version history.
|
4
4
|
|
@@ -114,6 +114,15 @@ Sandal.default! ignore_signature: true, ignore_exp: true
|
|
114
114
|
|
115
115
|
These options can also be configured on a per-token basis by using a second `options` parameter in the block passed to the `decode` method.
|
116
116
|
|
117
|
+
Note that by default the library requires that the innermost token is signed as this is the most secure option. To enable decoding tokens that don't meet this policy you can disable it as shown below, although I'd strongly recommend that you just allow the token to be rejected!
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
payload = Sandal.decode_token(unsafe_token) do |header, options|
|
121
|
+
options[:signature_policy] = :none
|
122
|
+
Sandal::Sig::NONE
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
117
126
|
## Contributing
|
118
127
|
|
119
128
|
1. Fork it
|
data/lib/sandal.rb
CHANGED
@@ -50,6 +50,10 @@ module Sandal
|
|
50
50
|
# max_clock_skew::
|
51
51
|
# The maximum clock skew, in seconds, when validating times. If your server time is out of sync with the token
|
52
52
|
# server then this can be increased to take that into account. It probably shouldn't be more than about 300.
|
53
|
+
# signature_policy::
|
54
|
+
# The policy for requiring signatures in tokens. The possible values are:
|
55
|
+
# - :strict (default) - The innermost token must be signed. This is the recommended policy.
|
56
|
+
# - :none - No signature is required. This _really_ isn't recommended.
|
53
57
|
# valid_iss::
|
54
58
|
# A list of valid token issuers, if validation of the issuer claim is required.
|
55
59
|
# valid_aud::
|
@@ -59,6 +63,7 @@ module Sandal
|
|
59
63
|
ignore_nbf: false,
|
60
64
|
ignore_signature: false,
|
61
65
|
max_clock_skew: 0,
|
66
|
+
signature_policy: :strict,
|
62
67
|
valid_iss: [],
|
63
68
|
valid_aud: []
|
64
69
|
}
|
@@ -188,6 +193,9 @@ module Sandal
|
|
188
193
|
payload
|
189
194
|
end
|
190
195
|
else
|
196
|
+
if options[:signature_policy] == :strict && !is_signed?(parts)
|
197
|
+
raise Sandal::UnsupportedTokenError, "The innermost token is not signed."
|
198
|
+
end
|
191
199
|
parse_and_validate(payload, options)
|
192
200
|
end
|
193
201
|
end
|
@@ -196,10 +204,10 @@ module Sandal
|
|
196
204
|
|
197
205
|
# Decodes and validates a signed JSON Web Token.
|
198
206
|
def self.validate_signature(parts, signature, validator)
|
199
|
-
|
207
|
+
raise UnsupportedTokenError, "Unsupported signature method." if validator.nil?
|
200
208
|
secured_input = parts.take(2).join(".")
|
201
209
|
unless validator.valid?(signature, secured_input)
|
202
|
-
raise
|
210
|
+
raise InvalidTokenError, "Invalid signature."
|
203
211
|
end
|
204
212
|
end
|
205
213
|
|
@@ -209,7 +217,7 @@ module Sandal
|
|
209
217
|
parts[0] = Sandal::Json.load(parts[0])
|
210
218
|
parts
|
211
219
|
rescue
|
212
|
-
raise
|
220
|
+
raise InvalidTokenError, "Invalid token encoding."
|
213
221
|
end
|
214
222
|
|
215
223
|
# Parses the content of a token and validates the claims if is JSON claims.
|
data/lib/sandal/version.rb
CHANGED
data/sandal.gemspec
CHANGED
@@ -28,5 +28,5 @@ Gem::Specification.new do |s|
|
|
28
28
|
s.add_development_dependency "redcarpet", ">= 2.2" unless RUBY_PLATFORM == "java" # for yard
|
29
29
|
s.add_development_dependency "kramdown", ">= 1.0" if RUBY_PLATFORM == "java" # for yard
|
30
30
|
|
31
|
-
s.requirements << "
|
31
|
+
s.requirements << "OpenSSL 1.0.1c for EC signature methods (1.0.1f recommended)"
|
32
32
|
end
|
data/spec/sandal_spec.rb
CHANGED
@@ -10,7 +10,8 @@ describe Sandal do
|
|
10
10
|
private_key = OpenSSL::PKey::RSA.new(2048)
|
11
11
|
encrypter = Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA1_5.new(private_key.public_key))
|
12
12
|
token = Sandal.encrypt_token(payload, encrypter, { 'zip' => 'DEF' })
|
13
|
-
decoded_payload = Sandal.decode_token(token) do |header|
|
13
|
+
decoded_payload = Sandal.decode_token(token) do |header, options|
|
14
|
+
options[:signature_policy] = :none
|
14
15
|
Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA1_5.new(private_key))
|
15
16
|
end
|
16
17
|
expect(decoded_payload).to eq(payload)
|
@@ -32,6 +33,28 @@ describe Sandal do
|
|
32
33
|
|
33
34
|
end
|
34
35
|
|
36
|
+
context "#decode_token" do
|
37
|
+
|
38
|
+
it 'does not accept tokens with no signatures by default' do
|
39
|
+
payload = 'some payload to be zipped'
|
40
|
+
token = Sandal.encode_token(payload, Sandal::Sig::NONE)
|
41
|
+
expect { Sandal.decode_token(token) do |header, options|
|
42
|
+
Sandal::Sig::NONE
|
43
|
+
end }.to raise_error Sandal::UnsupportedTokenError
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'does not accept encrypted tokens with no signatures by default' do
|
47
|
+
payload = 'some payload to be zipped'
|
48
|
+
private_key = OpenSSL::PKey::RSA.new(2048)
|
49
|
+
encrypter = Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA1_5.new(private_key.public_key))
|
50
|
+
token = Sandal.encrypt_token(payload, encrypter)
|
51
|
+
expect { Sandal.decode_token(token) do |header, options|
|
52
|
+
Sandal::Enc::A128CBC_HS256.new(Sandal::Enc::Alg::RSA1_5.new(private_key))
|
53
|
+
end }.to raise_error Sandal::UnsupportedTokenError
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
35
58
|
it 'raises a token error when the token format is invalid' do
|
36
59
|
expect { Sandal.decode_token('not a valid token') }.to raise_error Sandal::TokenError
|
37
60
|
end
|
@@ -43,42 +66,61 @@ describe Sandal do
|
|
43
66
|
it 'encodes and decodes tokens with no signature' do
|
44
67
|
payload = 'Hello, World'
|
45
68
|
token = Sandal.encode_token(payload, nil)
|
46
|
-
decoded_payload = Sandal.decode_token(token)
|
69
|
+
decoded_payload = Sandal.decode_token(token) do |header, options|
|
70
|
+
options[:signature_policy] = :none
|
71
|
+
Sandal::Sig::NONE
|
72
|
+
end
|
47
73
|
expect(decoded_payload).to eq(payload)
|
48
74
|
end
|
49
75
|
|
50
76
|
it 'encodes and decodes tokens with "none" signature' do
|
51
77
|
payload = 'Hello, World'
|
52
|
-
token = Sandal.encode_token(payload, Sandal::Sig::
|
53
|
-
decoded_payload = Sandal.decode_token(token)
|
78
|
+
token = Sandal.encode_token(payload, Sandal::Sig::NONE)
|
79
|
+
decoded_payload = Sandal.decode_token(token) do |header, options|
|
80
|
+
options[:signature_policy] = :none
|
81
|
+
Sandal::Sig::NONE
|
82
|
+
end
|
54
83
|
expect(decoded_payload).to eq(payload)
|
55
84
|
end
|
56
85
|
|
57
86
|
it 'decodes non-JSON payloads to a String' do
|
58
87
|
token = Sandal.encode_token('not valid json', nil)
|
59
|
-
expect(Sandal.decode_token(token)
|
88
|
+
expect(Sandal.decode_token(token) do |header, options|
|
89
|
+
options[:signature_policy] = :none
|
90
|
+
Sandal::Sig::NONE
|
91
|
+
end).to be_kind_of String
|
60
92
|
end
|
61
93
|
|
62
94
|
it 'decodes JSON payloads to a Hash' do
|
63
95
|
token = Sandal.encode_token({ 'valid' => 'json' }, nil)
|
64
|
-
expect(Sandal.decode_token(token)
|
96
|
+
expect(Sandal.decode_token(token) do |header, options|
|
97
|
+
options[:signature_policy] = :none
|
98
|
+
Sandal::Sig::NONE
|
99
|
+
end).to be_kind_of Hash
|
65
100
|
end
|
66
101
|
|
67
102
|
it 'raises a claim error when the expiry date is far in the past' do
|
68
103
|
token = Sandal.encode_token({ 'exp' => (Time.now - 600).to_i }, nil)
|
69
|
-
expect { Sandal.decode_token(token)
|
104
|
+
expect { Sandal.decode_token(token) do |header, options|
|
105
|
+
options[:signature_policy] = :none
|
106
|
+
Sandal::Sig::NONE
|
107
|
+
end }.to raise_error Sandal::ClaimError
|
70
108
|
end
|
71
109
|
|
72
110
|
it 'raises a claim error when the expiry date is invalid' do
|
73
111
|
token = Sandal.encode_token({ 'exp' => 'invalid value' }, nil)
|
74
|
-
expect { Sandal.decode_token(token)
|
112
|
+
expect { Sandal.decode_token(token) do |header, options|
|
113
|
+
options[:signature_policy] = :none
|
114
|
+
Sandal::Sig::NONE
|
115
|
+
end }.to raise_error Sandal::ClaimError
|
75
116
|
end
|
76
117
|
|
77
118
|
it 'does not raise an error when the expiry date is in the past but validation is disabled' do
|
78
119
|
token = Sandal.encode_token({ 'exp' => (Time.now - 600).to_i }, nil)
|
79
120
|
Sandal.decode_token(token) do |header, options|
|
80
121
|
options[:ignore_exp] = true
|
81
|
-
|
122
|
+
options[:signature_policy] = :none
|
123
|
+
Sandal::Sig::NONE
|
82
124
|
end
|
83
125
|
end
|
84
126
|
|
@@ -86,30 +128,41 @@ describe Sandal do
|
|
86
128
|
token = Sandal.encode_token({ 'exp' => (Time.now - 60).to_i }, nil)
|
87
129
|
Sandal.decode_token(token) do |header, options|
|
88
130
|
options[:max_clock_skew] = 300
|
89
|
-
|
131
|
+
options[:signature_policy] = :none
|
132
|
+
Sandal::Sig::NONE
|
90
133
|
end
|
91
134
|
end
|
92
135
|
|
93
136
|
it 'does not raise an error when the expiry date is valid' do
|
94
137
|
token = Sandal.encode_token({ 'exp' => (Time.now + 60).to_i }, nil)
|
95
|
-
Sandal.decode_token(token)
|
138
|
+
Sandal.decode_token(token) do |header, options|
|
139
|
+
options[:signature_policy] = :none
|
140
|
+
Sandal::Sig::NONE
|
141
|
+
end
|
96
142
|
end
|
97
143
|
|
98
144
|
it 'raises a claim error when the not-before date is far in the future' do
|
99
145
|
token = Sandal.encode_token({ 'nbf' => (Time.now + 600).to_i }, nil)
|
100
|
-
expect { Sandal.decode_token(token)
|
146
|
+
expect { Sandal.decode_token(token) do |header, options|
|
147
|
+
options[:signature_policy] = :none
|
148
|
+
Sandal::Sig::NONE
|
149
|
+
end }.to raise_error Sandal::ClaimError
|
101
150
|
end
|
102
151
|
|
103
152
|
it 'raises a claim error when the not-before date is invalid' do
|
104
153
|
token = Sandal.encode_token({ 'nbf' => 'invalid value' }, nil)
|
105
|
-
expect { Sandal.decode_token(token)
|
154
|
+
expect { Sandal.decode_token(token) do |header, options|
|
155
|
+
options[:signature_policy] = :none
|
156
|
+
Sandal::Sig::NONE
|
157
|
+
end }.to raise_error Sandal::ClaimError
|
106
158
|
end
|
107
159
|
|
108
160
|
it 'does not raise an error when the not-before date is in the future but validation is disabled' do
|
109
161
|
token = Sandal.encode_token({ 'nbf' => (Time.now + 600).to_i }, nil)
|
110
162
|
Sandal.decode_token(token) do |header, options|
|
111
163
|
options[:ignore_nbf] = true
|
112
|
-
|
164
|
+
options[:signature_policy] = :none
|
165
|
+
Sandal::Sig::NONE
|
113
166
|
end
|
114
167
|
end
|
115
168
|
|
@@ -117,20 +170,25 @@ describe Sandal do
|
|
117
170
|
token = Sandal.encode_token({ 'nbf' => (Time.now + 60).to_i }, nil)
|
118
171
|
Sandal.decode_token(token) do |header, options|
|
119
172
|
options[:max_clock_skew] = 300
|
120
|
-
|
173
|
+
options[:signature_policy] = :none
|
174
|
+
Sandal::Sig::NONE
|
121
175
|
end
|
122
176
|
end
|
123
177
|
|
124
178
|
it 'does not raise an error when the not-before is valid' do
|
125
179
|
token = Sandal.encode_token({ 'nbf' => (Time.now - 60).to_i }, nil)
|
126
|
-
Sandal.decode_token(token)
|
180
|
+
Sandal.decode_token(token) do |header, options|
|
181
|
+
options[:signature_policy] = :none
|
182
|
+
Sandal::Sig::NONE
|
183
|
+
end
|
127
184
|
end
|
128
185
|
|
129
186
|
it 'raises a claim error when the issuer is not valid' do
|
130
187
|
token = Sandal.encode_token({ 'iss' => 'example.org' }, nil)
|
131
188
|
expect { Sandal.decode_token(token) do |header, options|
|
132
189
|
options[:valid_iss] = ['example.net']
|
133
|
-
|
190
|
+
options[:signature_policy] = :none
|
191
|
+
Sandal::Sig::NONE
|
134
192
|
end }.to raise_error Sandal::ClaimError
|
135
193
|
end
|
136
194
|
|
@@ -138,7 +196,8 @@ describe Sandal do
|
|
138
196
|
token = Sandal.encode_token({ 'iss' => 'example.org' }, nil)
|
139
197
|
Sandal.decode_token(token) do |header, options|
|
140
198
|
options[:valid_iss] = ['example.org', 'example.com']
|
141
|
-
|
199
|
+
options[:signature_policy] = :none
|
200
|
+
Sandal::Sig::NONE
|
142
201
|
end
|
143
202
|
end
|
144
203
|
|
@@ -146,7 +205,8 @@ describe Sandal do
|
|
146
205
|
token = Sandal.encode_token({ 'aud' => 'example.com' }, nil)
|
147
206
|
expect { Sandal.decode_token(token) do |header, options|
|
148
207
|
options[:valid_aud] = ['example.net']
|
149
|
-
|
208
|
+
options[:signature_policy] = :none
|
209
|
+
Sandal::Sig::NONE
|
150
210
|
end }.to raise_error Sandal::ClaimError
|
151
211
|
end
|
152
212
|
|
@@ -154,7 +214,8 @@ describe Sandal do
|
|
154
214
|
token = Sandal.encode_token({ 'aud' => ['example.org', 'example.com'] }, nil)
|
155
215
|
expect { Sandal.decode_token(token) do |header, options|
|
156
216
|
options[:valid_aud] = ['example.net']
|
157
|
-
|
217
|
+
options[:signature_policy] = :none
|
218
|
+
Sandal::Sig::NONE
|
158
219
|
end }.to raise_error Sandal::ClaimError
|
159
220
|
end
|
160
221
|
|
@@ -162,7 +223,8 @@ describe Sandal do
|
|
162
223
|
token = Sandal.encode_token({ 'aud' => 'example.net' }, nil)
|
163
224
|
Sandal.decode_token(token) do |header, options|
|
164
225
|
options[:valid_aud] = ['example.net']
|
165
|
-
|
226
|
+
options[:signature_policy] = :none
|
227
|
+
Sandal::Sig::NONE
|
166
228
|
end
|
167
229
|
end
|
168
230
|
|
@@ -170,7 +232,8 @@ describe Sandal do
|
|
170
232
|
token = Sandal.encode_token({ 'aud' => ['example.com', 'example.net'] }, nil)
|
171
233
|
Sandal.decode_token(token) do |header, options|
|
172
234
|
options[:valid_aud] = ['example.net']
|
173
|
-
|
235
|
+
options[:signature_policy] = :none
|
236
|
+
Sandal::Sig::NONE
|
174
237
|
end
|
175
238
|
end
|
176
239
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sandal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Beech
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -178,9 +178,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
178
178
|
- !ruby/object:Gem::Version
|
179
179
|
version: '0'
|
180
180
|
requirements:
|
181
|
-
-
|
181
|
+
- OpenSSL 1.0.1c for EC signature methods (1.0.1f recommended)
|
182
182
|
rubyforge_project:
|
183
|
-
rubygems_version: 2.2.
|
183
|
+
rubygems_version: 2.2.2
|
184
184
|
signing_key:
|
185
185
|
specification_version: 4
|
186
186
|
summary: A JSON Web Token (JWT) library.
|