jwt 2.1.0 → 2.2.0.pre.beta.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.gitignore +1 -1
- data/.travis.yml +9 -3
- data/AUTHORS +84 -0
- data/Appraisals +14 -0
- data/CHANGELOG.md +77 -8
- data/README.md +96 -85
- data/lib/jwt.rb +9 -42
- data/lib/jwt/algos/ecdsa.rb +1 -1
- data/lib/jwt/algos/ps.rb +43 -0
- data/lib/jwt/base64.rb +19 -0
- data/lib/jwt/claims_validator.rb +33 -0
- data/lib/jwt/decode.rb +76 -25
- data/lib/jwt/encode.rb +42 -25
- data/lib/jwt/error.rb +16 -12
- data/lib/jwt/json.rb +18 -0
- data/lib/jwt/jwk.rb +31 -0
- data/lib/jwt/jwk/key_finder.rb +57 -0
- data/lib/jwt/jwk/rsa.rb +45 -0
- data/lib/jwt/security_utils.rb +6 -0
- data/lib/jwt/signature.rb +2 -0
- data/lib/jwt/verify.rb +1 -5
- data/lib/jwt/version.rb +3 -3
- data/ruby-jwt.gemspec +6 -3
- metadata +44 -58
- data/.reek.yml +0 -40
- data/Manifest +0 -8
- data/spec/fixtures/certs/ec256-private.pem +0 -8
- data/spec/fixtures/certs/ec256-public.pem +0 -4
- data/spec/fixtures/certs/ec256-wrong-private.pem +0 -8
- data/spec/fixtures/certs/ec256-wrong-public.pem +0 -4
- data/spec/fixtures/certs/ec384-private.pem +0 -9
- data/spec/fixtures/certs/ec384-public.pem +0 -5
- data/spec/fixtures/certs/ec384-wrong-private.pem +0 -9
- data/spec/fixtures/certs/ec384-wrong-public.pem +0 -5
- data/spec/fixtures/certs/ec512-private.pem +0 -10
- data/spec/fixtures/certs/ec512-public.pem +0 -6
- data/spec/fixtures/certs/ec512-wrong-private.pem +0 -10
- data/spec/fixtures/certs/ec512-wrong-public.pem +0 -6
- data/spec/fixtures/certs/rsa-1024-private.pem +0 -15
- data/spec/fixtures/certs/rsa-1024-public.pem +0 -6
- data/spec/fixtures/certs/rsa-2048-private.pem +0 -27
- data/spec/fixtures/certs/rsa-2048-public.pem +0 -9
- data/spec/fixtures/certs/rsa-2048-wrong-private.pem +0 -27
- data/spec/fixtures/certs/rsa-2048-wrong-public.pem +0 -9
- data/spec/fixtures/certs/rsa-4096-private.pem +0 -51
- data/spec/fixtures/certs/rsa-4096-public.pem +0 -14
- data/spec/integration/readme_examples_spec.rb +0 -202
- data/spec/jwt/verify_spec.rb +0 -232
- data/spec/jwt_spec.rb +0 -315
- data/spec/spec_helper.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: faa454cb34b8e65b8d83996ec036f63eba2c3b9a9239d1abbe1c975be9bec69f
|
4
|
+
data.tar.gz: 590d89a3825fd03d82f6fe076f4bef60c564322befac28de85abe6559568b942
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb9aba3a38c1c88d1359a243dee4fdbed12e36a802620c42d9a53489f9be86f90125fadd5159365440d0f1f94982c2b9270609c8167b20db6c2b0adb1ced6017
|
7
|
+
data.tar.gz: 63069e007053ccb569b2846adc4cc1587aac2a7a62d5716ec6eafc2a893988aa71e3fa5cd672ad9fb53cd9c2100f3ea8a21b8200afe27c76f555ecb490e4ce86
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -3,9 +3,15 @@ cache: bundler
|
|
3
3
|
dist: trusty
|
4
4
|
language: ruby
|
5
5
|
rvm:
|
6
|
-
- 2.
|
7
|
-
- 2.
|
8
|
-
- 2.
|
6
|
+
- 2.3
|
7
|
+
- 2.4
|
8
|
+
- 2.5
|
9
|
+
- 2.6
|
10
|
+
gemfiles:
|
11
|
+
- gemfiles/standalone.gemfile
|
12
|
+
- gemfiles/rails_5.0.gemfile
|
13
|
+
- gemfiles/rails_5.1.gemfile
|
14
|
+
- gemfiles/rails_5.2.gemfile
|
9
15
|
script: "bundle exec rspec && bundle exec codeclimate-test-reporter"
|
10
16
|
before_install:
|
11
17
|
- sudo add-apt-repository ppa:chris-lea/libsodium -y
|
data/AUTHORS
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
Tim Rudat
|
2
|
+
Jeff Lindsay
|
3
|
+
A.B
|
4
|
+
Emilio Cristalli
|
5
|
+
Bob Aman
|
6
|
+
Zane Shannon
|
7
|
+
Oliver
|
8
|
+
Paul Battley
|
9
|
+
Nikita Shatov
|
10
|
+
blackanger
|
11
|
+
Tyler Pickett
|
12
|
+
James Stonehill
|
13
|
+
Adam Michael
|
14
|
+
Ville Lautanala
|
15
|
+
Peter M. Goldstein
|
16
|
+
Joakim Antman
|
17
|
+
Korstiaan de Ridder
|
18
|
+
Klaas Jan Wierenga
|
19
|
+
Steve Sloan
|
20
|
+
Bill Mill
|
21
|
+
Erik Michaels-Ober
|
22
|
+
Brian Flethcer
|
23
|
+
Jurriaan Pruis
|
24
|
+
Kevin Olbrich
|
25
|
+
Larry Lv
|
26
|
+
Rodrigo López Dato
|
27
|
+
Steven Davidovitz
|
28
|
+
Tom Wey
|
29
|
+
lukas
|
30
|
+
ojab
|
31
|
+
sawyerzhang
|
32
|
+
wohlgejm
|
33
|
+
yann ARMAND
|
34
|
+
Jordan Brough
|
35
|
+
Juanito Fatas
|
36
|
+
Julio Lopez
|
37
|
+
Zuzanna Stolińska
|
38
|
+
Katelyn Kasperowicz
|
39
|
+
aarongray
|
40
|
+
B
|
41
|
+
Adam Greene
|
42
|
+
Lowell Kirsh
|
43
|
+
Lucas Mazza
|
44
|
+
Makoto Chiba
|
45
|
+
Manuel Bustillo
|
46
|
+
Marco Adkins
|
47
|
+
Micah Gates
|
48
|
+
Mike Eirih
|
49
|
+
Mike Pastore
|
50
|
+
Mingan
|
51
|
+
Mitch Birti
|
52
|
+
Nicolas Leger
|
53
|
+
Austin Kabiru
|
54
|
+
Artsiom Kuts
|
55
|
+
Arnaud Mesureur
|
56
|
+
Ariel Salomon
|
57
|
+
Rob Wygand
|
58
|
+
danielgrippi
|
59
|
+
Ryan Brushett
|
60
|
+
Ryan McIlmoyl
|
61
|
+
Aman Gupta
|
62
|
+
Steve Teti
|
63
|
+
revodoge
|
64
|
+
Taiki Sugawara
|
65
|
+
nycvotes-dev
|
66
|
+
Alexandr Kostrikov
|
67
|
+
Tobias Haar
|
68
|
+
Toby Pinder
|
69
|
+
rono23
|
70
|
+
Tomé Duarte
|
71
|
+
Travis Hunter
|
72
|
+
Alexander Boyd
|
73
|
+
Yuji Yaginuma
|
74
|
+
Ernie Miller
|
75
|
+
Evgeni Golov
|
76
|
+
Ewoud Kohl van Wijngaarden
|
77
|
+
Ilyaaaaaaaaaaaaa Zhitomirskiy
|
78
|
+
Dorian Marié
|
79
|
+
Dave Grijalva
|
80
|
+
Jens Hausherr
|
81
|
+
Jeremiah Wuenschel
|
82
|
+
Brandon Keepers
|
83
|
+
John Downey
|
84
|
+
Josh Bodah
|
data/Appraisals
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,75 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## [2.
|
4
|
-
[Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.
|
3
|
+
## [2.2.0-beta.0](https://github.com/jwt/ruby-jwt/tree/2.2.0-beta.0) (2019-03-20)
|
4
|
+
[Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.1.0...2.2.0-beta.0)
|
5
|
+
|
6
|
+
**Implemented enhancements:**
|
7
|
+
|
8
|
+
- Use iat\_leeway option [\#273](https://github.com/jwt/ruby-jwt/issues/273)
|
9
|
+
- Use of global state in latest version breaks thread safety of JWT.decode [\#268](https://github.com/jwt/ruby-jwt/issues/268)
|
10
|
+
- JSON support [\#246](https://github.com/jwt/ruby-jwt/issues/246)
|
11
|
+
- Change the Github homepage URL to https [\#301](https://github.com/jwt/ruby-jwt/pull/301) ([ekohl](https://github.com/ekohl))
|
12
|
+
- Fix Salt length for conformance with PS family specification. [\#300](https://github.com/jwt/ruby-jwt/pull/300) ([tobypinder](https://github.com/tobypinder))
|
13
|
+
- Add support for Ruby 2.6 [\#299](https://github.com/jwt/ruby-jwt/pull/299) ([bustikiller](https://github.com/bustikiller))
|
14
|
+
- update homepage in gemspec to use HTTPS [\#298](https://github.com/jwt/ruby-jwt/pull/298) ([evgeni](https://github.com/evgeni))
|
15
|
+
- Make sure alg parameter value isn't added twice [\#297](https://github.com/jwt/ruby-jwt/pull/297) ([korstiaan](https://github.com/korstiaan))
|
16
|
+
- Claims Validation [\#295](https://github.com/jwt/ruby-jwt/pull/295) ([jamesstonehill](https://github.com/jamesstonehill))
|
17
|
+
- JWT::Encode refactorings, alg and exp related bugfixes [\#293](https://github.com/jwt/ruby-jwt/pull/293) ([anakinj](https://github.com/anakinj))
|
18
|
+
- Proposal of simple JWK support [\#289](https://github.com/jwt/ruby-jwt/pull/289) ([anakinj](https://github.com/anakinj))
|
19
|
+
- Add RSASSA-PSS signature signing support [\#285](https://github.com/jwt/ruby-jwt/pull/285) ([oliver-hohn](https://github.com/oliver-hohn))
|
20
|
+
- Add note about using a hard coded algorithm in README [\#280](https://github.com/jwt/ruby-jwt/pull/280) ([revodoge](https://github.com/revodoge))
|
21
|
+
- Add Appraisal support [\#278](https://github.com/jwt/ruby-jwt/pull/278) ([olbrich](https://github.com/olbrich))
|
22
|
+
- Fix decode threading issue [\#269](https://github.com/jwt/ruby-jwt/pull/269) ([ab320012](https://github.com/ab320012))
|
23
|
+
- Removed leeway from verify\_iat [\#257](https://github.com/jwt/ruby-jwt/pull/257) ([ab320012](https://github.com/ab320012))
|
24
|
+
|
25
|
+
**Fixed bugs:**
|
26
|
+
|
27
|
+
- Inconsistent handling of payload claim data types [\#282](https://github.com/jwt/ruby-jwt/issues/282)
|
28
|
+
- Use iat\\_leeway option [\#273](https://github.com/jwt/ruby-jwt/issues/273)
|
29
|
+
- Issued at validation [\#247](https://github.com/jwt/ruby-jwt/issues/247)
|
30
|
+
- Fix bug and simplify segment validation [\#292](https://github.com/jwt/ruby-jwt/pull/292) ([anakinj](https://github.com/anakinj))
|
31
|
+
- Removed leeway from verify\\_iat [\#257](https://github.com/jwt/ruby-jwt/pull/257) ([ab320012](https://github.com/ab320012))
|
32
|
+
|
33
|
+
**Closed issues:**
|
34
|
+
|
35
|
+
- RS256, public and private keys [\#291](https://github.com/jwt/ruby-jwt/issues/291)
|
36
|
+
- Allow passing current time to `decode` [\#288](https://github.com/jwt/ruby-jwt/issues/288)
|
37
|
+
- Verify exp claim without verifying jwt [\#281](https://github.com/jwt/ruby-jwt/issues/281)
|
38
|
+
- Decoding JWT with ES256 and secp256k1 curve [\#277](https://github.com/jwt/ruby-jwt/issues/277)
|
39
|
+
- Audience as an array - how to specify? [\#276](https://github.com/jwt/ruby-jwt/issues/276)
|
40
|
+
- signature validation using decode method for JWT [\#271](https://github.com/jwt/ruby-jwt/issues/271)
|
41
|
+
- JWT is easily breakable [\#267](https://github.com/jwt/ruby-jwt/issues/267)
|
42
|
+
- Ruby JWT Token [\#265](https://github.com/jwt/ruby-jwt/issues/265)
|
43
|
+
- ECDSA supported algorithms constant is defined as a string, not an array [\#264](https://github.com/jwt/ruby-jwt/issues/264)
|
44
|
+
- NoMethodError: undefined method `group' for \<xxxxx\> [\#261](https://github.com/jwt/ruby-jwt/issues/261)
|
45
|
+
- 'DecodeError'will replace 'ExpiredSignature' [\#260](https://github.com/jwt/ruby-jwt/issues/260)
|
46
|
+
- TypeError: no implicit conversion of OpenSSL::PKey::RSA into String [\#259](https://github.com/jwt/ruby-jwt/issues/259)
|
47
|
+
- NameError: uninitialized constant JWT::Algos::Eddsa::RbNaCl [\#258](https://github.com/jwt/ruby-jwt/issues/258)
|
48
|
+
- Get new token if curren token expired [\#256](https://github.com/jwt/ruby-jwt/issues/256)
|
49
|
+
- Infer algorithm from header [\#254](https://github.com/jwt/ruby-jwt/issues/254)
|
50
|
+
- Why is the result of decode is an array? [\#252](https://github.com/jwt/ruby-jwt/issues/252)
|
51
|
+
- Add support for headless token [\#251](https://github.com/jwt/ruby-jwt/issues/251)
|
52
|
+
- Leeway or exp\_leeway [\#215](https://github.com/jwt/ruby-jwt/issues/215)
|
53
|
+
- Could you describe purpose of cert fixtures and their cryptokey lengths. [\#185](https://github.com/jwt/ruby-jwt/issues/185)
|
54
|
+
|
55
|
+
**Merged pull requests:**
|
56
|
+
|
57
|
+
- Misc config improvements [\#296](https://github.com/jwt/ruby-jwt/pull/296) ([jamesstonehill](https://github.com/jamesstonehill))
|
58
|
+
- Fix JSON conflict between \#293 and \#292 [\#294](https://github.com/jwt/ruby-jwt/pull/294) ([anakinj](https://github.com/anakinj))
|
59
|
+
- Drop Ruby 2.2 from test matrix [\#290](https://github.com/jwt/ruby-jwt/pull/290) ([anakinj](https://github.com/anakinj))
|
60
|
+
- Remove broken reek config [\#283](https://github.com/jwt/ruby-jwt/pull/283) ([excpt](https://github.com/excpt))
|
61
|
+
- Add missing test, Update common files [\#275](https://github.com/jwt/ruby-jwt/pull/275) ([excpt](https://github.com/excpt))
|
62
|
+
- Remove iat\_leeway option [\#274](https://github.com/jwt/ruby-jwt/pull/274) ([wohlgejm](https://github.com/wohlgejm))
|
63
|
+
- improving code quality of jwt module [\#266](https://github.com/jwt/ruby-jwt/pull/266) ([ab320012](https://github.com/ab320012))
|
64
|
+
- fixed ECDSA supported versions const [\#263](https://github.com/jwt/ruby-jwt/pull/263) ([starbeast](https://github.com/starbeast))
|
65
|
+
- Added my name to contributor list [\#262](https://github.com/jwt/ruby-jwt/pull/262) ([ab320012](https://github.com/ab320012))
|
66
|
+
- Use `Class\#new` Shorthand For Error Subclasses [\#255](https://github.com/jwt/ruby-jwt/pull/255) ([akabiru](https://github.com/akabiru))
|
67
|
+
- \[CI\] Test against Ruby 2.5 [\#253](https://github.com/jwt/ruby-jwt/pull/253) ([nicolasleger](https://github.com/nicolasleger))
|
68
|
+
- Fix README [\#250](https://github.com/jwt/ruby-jwt/pull/250) ([rono23](https://github.com/rono23))
|
69
|
+
- Fix link format [\#248](https://github.com/jwt/ruby-jwt/pull/248) ([y-yagi](https://github.com/y-yagi))
|
70
|
+
|
71
|
+
## [v2.1.0](https://github.com/jwt/ruby-jwt/tree/v2.1.0) (2017-10-06)
|
72
|
+
[Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.0.0...v2.1.0)
|
5
73
|
|
6
74
|
**Implemented enhancements:**
|
7
75
|
|
@@ -26,6 +94,7 @@
|
|
26
94
|
|
27
95
|
**Merged pull requests:**
|
28
96
|
|
97
|
+
- Release 2.1.0 preparations [\#243](https://github.com/jwt/ruby-jwt/pull/243) ([excpt](https://github.com/excpt))
|
29
98
|
- Update README.md [\#242](https://github.com/jwt/ruby-jwt/pull/242) ([excpt](https://github.com/excpt))
|
30
99
|
- Update ebert configuration [\#232](https://github.com/jwt/ruby-jwt/pull/232) ([excpt](https://github.com/excpt))
|
31
100
|
- added algos/strategy classes + structs for inputs [\#230](https://github.com/jwt/ruby-jwt/pull/230) ([ab320012](https://github.com/ab320012))
|
@@ -74,9 +143,9 @@
|
|
74
143
|
- Refactor [\#196](https://github.com/jwt/ruby-jwt/pull/196) ([EmilioCristalli](https://github.com/EmilioCristalli))
|
75
144
|
- Move signature logic to its own module [\#195](https://github.com/jwt/ruby-jwt/pull/195) ([EmilioCristalli](https://github.com/EmilioCristalli))
|
76
145
|
- Add options for claim-specific leeway [\#187](https://github.com/jwt/ruby-jwt/pull/187) ([EmilioCristalli](https://github.com/EmilioCristalli))
|
77
|
-
- Add user friendly encode error if private key is a String, \#171 [\#176](https://github.com/jwt/ruby-jwt/pull/176) ([
|
78
|
-
- Return empty string if signature less than byte\_size \#155 [\#175](https://github.com/jwt/ruby-jwt/pull/175) ([
|
79
|
-
- Remove 'typ' optional parameter [\#174](https://github.com/jwt/ruby-jwt/pull/174) ([
|
146
|
+
- Add user friendly encode error if private key is a String, \#171 [\#176](https://github.com/jwt/ruby-jwt/pull/176) ([ogonki-vetochki](https://github.com/ogonki-vetochki))
|
147
|
+
- Return empty string if signature less than byte\_size \#155 [\#175](https://github.com/jwt/ruby-jwt/pull/175) ([ogonki-vetochki](https://github.com/ogonki-vetochki))
|
148
|
+
- Remove 'typ' optional parameter [\#174](https://github.com/jwt/ruby-jwt/pull/174) ([ogonki-vetochki](https://github.com/ogonki-vetochki))
|
80
149
|
- Pass payload to keyfinder [\#172](https://github.com/jwt/ruby-jwt/pull/172) ([CodeMonkeySteve](https://github.com/CodeMonkeySteve))
|
81
150
|
- Use RbNaCl for HMAC if available with fallback to OpenSSL [\#149](https://github.com/jwt/ruby-jwt/pull/149) ([mwpastore](https://github.com/mwpastore))
|
82
151
|
|
@@ -94,7 +163,6 @@
|
|
94
163
|
- Signature is different at each run [\#190](https://github.com/jwt/ruby-jwt/issues/190)
|
95
164
|
- Include custom headers with password [\#189](https://github.com/jwt/ruby-jwt/issues/189)
|
96
165
|
- can't create token - 'NotImplementedError: Unsupported signing method' [\#186](https://github.com/jwt/ruby-jwt/issues/186)
|
97
|
-
- Why jwt depends on json \< 2.0 ? [\#179](https://github.com/jwt/ruby-jwt/issues/179)
|
98
166
|
- Cannot verify JWT at all?? [\#177](https://github.com/jwt/ruby-jwt/issues/177)
|
99
167
|
- verify\_iss: true is raising JWT::DecodeError instead of JWT::InvalidIssuerError [\#170](https://github.com/jwt/ruby-jwt/issues/170)
|
100
168
|
|
@@ -106,12 +174,12 @@
|
|
106
174
|
- Add minimum required ruby version to gemspec [\#193](https://github.com/jwt/ruby-jwt/pull/193) ([excpt](https://github.com/excpt))
|
107
175
|
- Code smell fixes [\#192](https://github.com/jwt/ruby-jwt/pull/192) ([excpt](https://github.com/excpt))
|
108
176
|
- Version bump to 2.0.0.dev [\#191](https://github.com/jwt/ruby-jwt/pull/191) ([excpt](https://github.com/excpt))
|
109
|
-
- Basic encode module refactoring \#121 [\#182](https://github.com/jwt/ruby-jwt/pull/182) ([
|
177
|
+
- Basic encode module refactoring \#121 [\#182](https://github.com/jwt/ruby-jwt/pull/182) ([ogonki-vetochki](https://github.com/ogonki-vetochki))
|
110
178
|
- Fix travis ci build configuration [\#181](https://github.com/jwt/ruby-jwt/pull/181) ([excpt](https://github.com/excpt))
|
111
179
|
- Fix travis ci build configuration [\#180](https://github.com/jwt/ruby-jwt/pull/180) ([excpt](https://github.com/excpt))
|
112
180
|
- Fix typo in README [\#178](https://github.com/jwt/ruby-jwt/pull/178) ([tomeduarte](https://github.com/tomeduarte))
|
113
181
|
- Fix code style [\#173](https://github.com/jwt/ruby-jwt/pull/173) ([excpt](https://github.com/excpt))
|
114
|
-
- Fixed a typo in a spec name [\#169](https://github.com/jwt/ruby-jwt/pull/169) ([
|
182
|
+
- Fixed a typo in a spec name [\#169](https://github.com/jwt/ruby-jwt/pull/169) ([mingan](https://github.com/mingan))
|
115
183
|
|
116
184
|
## [v1.5.6](https://github.com/jwt/ruby-jwt/tree/v1.5.6) (2016-09-19)
|
117
185
|
[Full Changelog](https://github.com/jwt/ruby-jwt/compare/v1.5.5...v1.5.6)
|
@@ -367,6 +435,7 @@
|
|
367
435
|
|
368
436
|
**Closed issues:**
|
369
437
|
|
438
|
+
- yanking of version 0.1.12 causes issues [\#39](https://github.com/jwt/ruby-jwt/issues/39)
|
370
439
|
- Semantic versioning [\#37](https://github.com/jwt/ruby-jwt/issues/37)
|
371
440
|
- Update gem to get latest changes [\#36](https://github.com/jwt/ruby-jwt/issues/36)
|
372
441
|
|
data/README.md
CHANGED
@@ -5,8 +5,9 @@
|
|
5
5
|
[![Code Climate](https://codeclimate.com/github/jwt/ruby-jwt/badges/gpa.svg)](https://codeclimate.com/github/jwt/ruby-jwt)
|
6
6
|
[![Test Coverage](https://codeclimate.com/github/jwt/ruby-jwt/badges/coverage.svg)](https://codeclimate.com/github/jwt/ruby-jwt/coverage)
|
7
7
|
[![Issue Count](https://codeclimate.com/github/jwt/ruby-jwt/badges/issue_count.svg)](https://codeclimate.com/github/jwt/ruby-jwt)
|
8
|
+
[![Ebert](https://ebertapp.io/github/jwt/ruby-jwt.svg)](https://ebertapp.io/github/jwt/ruby-jwt)
|
8
9
|
|
9
|
-
A
|
10
|
+
A ruby implementation of the [RFC 7519 OAuth JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519) standard.
|
10
11
|
|
11
12
|
If you have further questions related to development or usage, join us: [ruby-jwt google group](https://groups.google.com/forum/#!forum/ruby-jwt).
|
12
13
|
|
@@ -31,7 +32,7 @@ And run `bundle install`
|
|
31
32
|
|
32
33
|
## Algorithms and Usage
|
33
34
|
|
34
|
-
The JWT spec supports NONE, HMAC, RSASSA, ECDSA and RSASSA-PSS algorithms for cryptographic signing. Currently the jwt gem supports NONE, HMAC, RSASSA and ECDSA. If you are using cryptographic signing, you need to specify the algorithm in the options hash whenever you call JWT.decode to ensure that an attacker [cannot bypass the algorithm verification step](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/).
|
35
|
+
The JWT spec supports NONE, HMAC, RSASSA, ECDSA and RSASSA-PSS algorithms for cryptographic signing. Currently the jwt gem supports NONE, HMAC, RSASSA and ECDSA. If you are using cryptographic signing, you need to specify the algorithm in the options hash whenever you call JWT.decode to ensure that an attacker [cannot bypass the algorithm verification step](https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/). **It is strongly recommended that you hard code the algorithm, as you may leave yourself vulnerable by dynamically picking the algorithm**
|
35
36
|
|
36
37
|
See: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values for JWS](https://tools.ietf.org/html/rfc7518#section-3.1)
|
37
38
|
|
@@ -42,12 +43,12 @@ See: [ JSON Web Algorithms (JWA) 3.1. "alg" (Algorithm) Header Parameter Values
|
|
42
43
|
```ruby
|
43
44
|
require 'jwt'
|
44
45
|
|
45
|
-
payload = {:
|
46
|
+
payload = { data: 'test' }
|
46
47
|
|
47
48
|
# IMPORTANT: set nil as password parameter
|
48
49
|
token = JWT.encode payload, nil, 'none'
|
49
50
|
|
50
|
-
#
|
51
|
+
# eyJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.
|
51
52
|
puts token
|
52
53
|
|
53
54
|
# Set password to nil and validation to false otherwise this won't work
|
@@ -73,10 +74,10 @@ hmac_secret = 'my$ecretK3y'
|
|
73
74
|
|
74
75
|
token = JWT.encode payload, hmac_secret, 'HS256'
|
75
76
|
|
76
|
-
#
|
77
|
+
# eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.pNIWIL34Jo13LViZAJACzK6Yf0qnvT_BuwOxiMCPE-Y
|
77
78
|
puts token
|
78
79
|
|
79
|
-
decoded_token = JWT.decode token, hmac_secret, true, { :
|
80
|
+
decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
|
80
81
|
|
81
82
|
# Array
|
82
83
|
# [
|
@@ -104,10 +105,10 @@ rsa_public = rsa_private.public_key
|
|
104
105
|
|
105
106
|
token = JWT.encode payload, rsa_private, 'RS256'
|
106
107
|
|
107
|
-
#
|
108
|
+
# eyJhbGciOiJSUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.GplO4w1spRgvEJQ3-FOtZr-uC8L45Jt7SN0J4woBnEXG_OZBSNcZjAJWpjadVYEe2ev3oUBFDYM1N_-0BTVeFGGYvMewu8E6aMjSZvOpf1cZBew-Vt4poSq7goG2YRI_zNPt3af2lkPqXD796IKC5URrEvcgF5xFQ-6h07XRDpSRx1ECrNsUOt7UM3l1IB4doY11GzwQA5sHDTmUZ0-kBT76ZMf12Srg_N3hZwphxBtudYtN5VGZn420sVrQMdPE_7Ni3EiWT88j7WCr1xrF60l8sZT3yKCVleG7D2BEXacTntB7GktBv4Xo8OKnpwpqTpIlC05dMowMkz3rEAAYbQ
|
108
109
|
puts token
|
109
110
|
|
110
|
-
decoded_token = JWT.decode token, rsa_public, true, { :
|
111
|
+
decoded_token = JWT.decode token, rsa_public, true, { algorithm: 'RS256' }
|
111
112
|
|
112
113
|
# Array
|
113
114
|
# [
|
@@ -131,10 +132,10 @@ ecdsa_public.private_key = nil
|
|
131
132
|
|
132
133
|
token = JWT.encode payload, ecdsa_key, 'ES256'
|
133
134
|
|
134
|
-
#
|
135
|
+
# eyJhbGciOiJFUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.AlLW--kaF7EX1NMX9WJRuIW8NeRJbn2BLXHns7Q5TZr7Hy3lF6MOpMlp7GoxBFRLISQ6KrD0CJOrR8aogEsPeg
|
135
136
|
puts token
|
136
137
|
|
137
|
-
decoded_token = JWT.decode token, ecdsa_public, true, { :
|
138
|
+
decoded_token = JWT.decode token, ecdsa_public, true, { algorithm: 'ES256' }
|
138
139
|
|
139
140
|
# Array
|
140
141
|
# [
|
@@ -154,17 +155,17 @@ gem 'rbnacl'
|
|
154
155
|
|
155
156
|
For more detailed installation instruction check the official [repository](https://github.com/cryptosphere/rbnacl) on GitHub.
|
156
157
|
|
157
|
-
* ED25519
|
158
|
+
* ED25519
|
158
159
|
|
159
|
-
```ruby
|
160
|
-
private_key = RbNaCl::Signatures::Ed25519::SigningKey.new(
|
160
|
+
```ruby
|
161
|
+
private_key = RbNaCl::Signatures::Ed25519::SigningKey.new('abcdefghijklmnopqrstuvwxyzABCDEF')
|
161
162
|
public_key = private_key.verify_key
|
162
|
-
token = JWT.encode payload, private_key, 'ED25519'
|
163
|
+
token = JWT.encode payload, private_key, 'ED25519'
|
163
164
|
|
164
|
-
# eyJhbGciOiJFRDI1NTE5In0.
|
165
|
+
# eyJhbGciOiJFRDI1NTE5In0.eyJkYXRhIjoidGVzdCJ9.6xIztXyOupskddGA_RvKU76V9b2dCQUYhoZEVFnRimJoPYIzZ2Fm47CWw8k2NTCNpgfAuxg9OXjaiVK7MvrbCQ
|
165
166
|
puts token
|
166
167
|
|
167
|
-
decoded_token = JWT.decode token, public_key, true, {:
|
168
|
+
decoded_token = JWT.decode token, public_key, true, { algorithm: 'ED25519' }
|
168
169
|
# Array
|
169
170
|
# [
|
170
171
|
# {"test"=>"data"}, # payload
|
@@ -175,7 +176,34 @@ decoded_token = JWT.decode token, public_key, true, {:algorithm => 'ED25519' }
|
|
175
176
|
|
176
177
|
**RSASSA-PSS**
|
177
178
|
|
178
|
-
|
179
|
+
In order to use this algorithm you need to add the `openssl` gem to you `Gemfile` with a version greater or equal to `2.1`.
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
gem 'openssl', '~> 2.1'
|
183
|
+
```
|
184
|
+
|
185
|
+
* PS256 - RSASSA-PSS using SHA-256 hash algorithm
|
186
|
+
* PS384 - RSASSA-PSS using SHA-384 hash algorithm
|
187
|
+
* PS512 - RSASSA-PSS using SHA-512 hash algorithm
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
rsa_private = OpenSSL::PKey::RSA.generate 2048
|
191
|
+
rsa_public = rsa_private.public_key
|
192
|
+
|
193
|
+
token = JWT.encode payload, rsa_private, 'PS256'
|
194
|
+
|
195
|
+
# eyJhbGciOiJQUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.KEmqagMUHM-NcmXo6818ZazVTIAkn9qU9KQFT1c5Iq91n0KRpAI84jj4ZCdkysDlWokFs3Dmn4MhcXP03oJKLFgnoPL40_Wgg9iFr0jnIVvnMUp1kp2RFUbL0jqExGTRA3LdAhuvw6ZByGD1bkcWjDXygjQw-hxILrT1bENjdr0JhFd-cB0-ps5SB0mwhFNcUw-OM3Uu30B1-mlFaelUY8jHJYKwLTZPNxHzndt8RGXF8iZLp7dGb06HSCKMcVzhASGMH4ZdFystRe2hh31cwcvnl-Eo_D4cdwmpN3Abhk_8rkxawQJR3duh8HNKc4AyFPo7SabEaSu2gLnLfN3yfg
|
196
|
+
puts token
|
197
|
+
|
198
|
+
decoded_token = JWT.decode token, rsa_public, true, { algorithm: 'PS256' }
|
199
|
+
|
200
|
+
# Array
|
201
|
+
# [
|
202
|
+
# {"data"=>"test"}, # payload
|
203
|
+
# {"alg"=>"PS256"} # header
|
204
|
+
# ]
|
205
|
+
puts decoded_token
|
206
|
+
```
|
179
207
|
|
180
208
|
## Support for reserved claim names
|
181
209
|
JSON Web Token defines some reserved claim names and defines how they should be
|
@@ -190,7 +218,7 @@ used. JWT supports these reserved claim names:
|
|
190
218
|
- 'sub' (Subject) Claim
|
191
219
|
|
192
220
|
## Add custom header fields
|
193
|
-
Ruby-jwt gem supports custom [header fields]
|
221
|
+
Ruby-jwt gem supports custom [header fields](https://tools.ietf.org/html/rfc7519#section-5)
|
194
222
|
To add custom header fields you need to pass `header_fields` parameter
|
195
223
|
|
196
224
|
```ruby
|
@@ -202,12 +230,12 @@ token = JWT.encode payload, key, algorithm='HS256', header_fields={}
|
|
202
230
|
```ruby
|
203
231
|
require 'jwt'
|
204
232
|
|
205
|
-
payload = {:
|
233
|
+
payload = { data: 'test' }
|
206
234
|
|
207
235
|
# IMPORTANT: set nil as password parameter
|
208
|
-
token = JWT.encode payload, nil, 'none', { :
|
236
|
+
token = JWT.encode payload, nil, 'none', { typ: 'JWT' }
|
209
237
|
|
210
|
-
#
|
238
|
+
# eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJkYXRhIjoidGVzdCJ9.
|
211
239
|
puts token
|
212
240
|
|
213
241
|
# Set password to nil and validation to false otherwise this won't work
|
@@ -231,12 +259,12 @@ From [Oauth JSON Web Token 4.1.4. "exp" (Expiration Time) Claim](https://tools.i
|
|
231
259
|
|
232
260
|
```ruby
|
233
261
|
exp = Time.now.to_i + 4 * 3600
|
234
|
-
exp_payload = { :
|
262
|
+
exp_payload = { data: 'data', exp: exp }
|
235
263
|
|
236
264
|
token = JWT.encode exp_payload, hmac_secret, 'HS256'
|
237
265
|
|
238
266
|
begin
|
239
|
-
decoded_token = JWT.decode token, hmac_secret, true, { :
|
267
|
+
decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
|
240
268
|
rescue JWT::ExpiredSignature
|
241
269
|
# Handle expired token, e.g. logout user or deny access
|
242
270
|
end
|
@@ -248,14 +276,14 @@ end
|
|
248
276
|
exp = Time.now.to_i - 10
|
249
277
|
leeway = 30 # seconds
|
250
278
|
|
251
|
-
exp_payload = { :
|
279
|
+
exp_payload = { data: 'data', exp: exp }
|
252
280
|
|
253
281
|
# build expired token
|
254
282
|
token = JWT.encode exp_payload, hmac_secret, 'HS256'
|
255
283
|
|
256
284
|
begin
|
257
285
|
# add leeway to ensure the token is still accepted
|
258
|
-
decoded_token = JWT.decode token, hmac_secret, true, { :
|
286
|
+
decoded_token = JWT.decode token, hmac_secret, true, { exp_leeway: leeway, algorithm: 'HS256' }
|
259
287
|
rescue JWT::ExpiredSignature
|
260
288
|
# Handle expired token, e.g. logout user or deny access
|
261
289
|
end
|
@@ -271,12 +299,12 @@ From [Oauth JSON Web Token 4.1.5. "nbf" (Not Before) Claim](https://tools.ietf.o
|
|
271
299
|
|
272
300
|
```ruby
|
273
301
|
nbf = Time.now.to_i - 3600
|
274
|
-
nbf_payload = { :
|
302
|
+
nbf_payload = { data: 'data', nbf: nbf }
|
275
303
|
|
276
304
|
token = JWT.encode nbf_payload, hmac_secret, 'HS256'
|
277
305
|
|
278
306
|
begin
|
279
|
-
decoded_token = JWT.decode token, hmac_secret, true, { :
|
307
|
+
decoded_token = JWT.decode token, hmac_secret, true, { algorithm: 'HS256' }
|
280
308
|
rescue JWT::ImmatureSignature
|
281
309
|
# Handle invalid token, e.g. logout user or deny access
|
282
310
|
end
|
@@ -288,14 +316,14 @@ end
|
|
288
316
|
nbf = Time.now.to_i + 10
|
289
317
|
leeway = 30
|
290
318
|
|
291
|
-
nbf_payload = { :
|
319
|
+
nbf_payload = { data: 'data', nbf: nbf }
|
292
320
|
|
293
321
|
# build expired token
|
294
322
|
token = JWT.encode nbf_payload, hmac_secret, 'HS256'
|
295
323
|
|
296
324
|
begin
|
297
325
|
# add leeway to ensure the token is valid
|
298
|
-
decoded_token = JWT.decode token, hmac_secret, true, { :
|
326
|
+
decoded_token = JWT.decode token, hmac_secret, true, { nbf_leeway: leeway, algorithm: 'HS256' }
|
299
327
|
rescue JWT::ImmatureSignature
|
300
328
|
# Handle invalid token, e.g. logout user or deny access
|
301
329
|
end
|
@@ -311,13 +339,13 @@ You can pass multiple allowed issuers as an Array, verification will pass if one
|
|
311
339
|
|
312
340
|
```ruby
|
313
341
|
iss = 'My Awesome Company Inc. or https://my.awesome.website/'
|
314
|
-
iss_payload = { :
|
342
|
+
iss_payload = { data: 'data', iss: iss }
|
315
343
|
|
316
344
|
token = JWT.encode iss_payload, hmac_secret, 'HS256'
|
317
345
|
|
318
346
|
begin
|
319
347
|
# Add iss to the validation to check if the token has been manipulated
|
320
|
-
decoded_token = JWT.decode token, hmac_secret, true, { :
|
348
|
+
decoded_token = JWT.decode token, hmac_secret, true, { iss: iss, verify_iss: true, algorithm: 'HS256' }
|
321
349
|
rescue JWT::InvalidIssuerError
|
322
350
|
# Handle invalid token, e.g. logout user or deny access
|
323
351
|
end
|
@@ -331,13 +359,13 @@ From [Oauth JSON Web Token 4.1.3. "aud" (Audience) Claim](https://tools.ietf.org
|
|
331
359
|
|
332
360
|
```ruby
|
333
361
|
aud = ['Young', 'Old']
|
334
|
-
aud_payload = { :
|
362
|
+
aud_payload = { data: 'data', aud: aud }
|
335
363
|
|
336
364
|
token = JWT.encode aud_payload, hmac_secret, 'HS256'
|
337
365
|
|
338
366
|
begin
|
339
367
|
# Add aud to the validation to check if the token has been manipulated
|
340
|
-
decoded_token = JWT.decode token, hmac_secret, true, { :
|
368
|
+
decoded_token = JWT.decode token, hmac_secret, true, { aud: aud, verify_aud: true, algorithm: 'HS256' }
|
341
369
|
rescue JWT::InvalidAudError
|
342
370
|
# Handle invalid token, e.g. logout user or deny access
|
343
371
|
puts 'Audience Error'
|
@@ -354,58 +382,38 @@ From [Oauth JSON Web Token 4.1.7. "jti" (JWT ID) Claim](https://tools.ietf.org/h
|
|
354
382
|
# Use the secret and iat to create a unique key per request to prevent replay attacks
|
355
383
|
jti_raw = [hmac_secret, iat].join(':').to_s
|
356
384
|
jti = Digest::MD5.hexdigest(jti_raw)
|
357
|
-
jti_payload = { :
|
385
|
+
jti_payload = { data: 'data', iat: iat, jti: jti }
|
358
386
|
|
359
387
|
token = JWT.encode jti_payload, hmac_secret, 'HS256'
|
360
388
|
|
361
389
|
begin
|
362
390
|
# If :verify_jti is true, validation will pass if a JTI is present
|
363
|
-
#decoded_token = JWT.decode token, hmac_secret, true, { :
|
391
|
+
#decoded_token = JWT.decode token, hmac_secret, true, { verify_jti: true, algorithm: 'HS256' }
|
364
392
|
# Alternatively, pass a proc with your own code to check if the JTI has already been used
|
365
|
-
decoded_token = JWT.decode token, hmac_secret, true, { :
|
393
|
+
decoded_token = JWT.decode token, hmac_secret, true, { verify_jti: proc { |jti| my_validation_method(jti) }, algorithm: 'HS256' }
|
366
394
|
rescue JWT::InvalidJtiError
|
367
395
|
# Handle invalid token, e.g. logout user or deny access
|
368
396
|
puts 'Error'
|
369
397
|
end
|
370
|
-
|
371
398
|
```
|
372
399
|
|
373
400
|
### Issued At Claim
|
374
401
|
|
375
402
|
From [Oauth JSON Web Token 4.1.6. "iat" (Issued At) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.6):
|
376
403
|
|
377
|
-
> The `iat` (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. Its value MUST be a number containing a ***NumericDate*** value. Use of this claim is OPTIONAL.
|
404
|
+
> The `iat` (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. The `leeway` option is not taken into account when verifying this claim. The `iat_leeway` option was removed in version 2.2.0. Its value MUST be a number containing a ***NumericDate*** value. Use of this claim is OPTIONAL.
|
378
405
|
|
379
406
|
**Handle Issued At Claim**
|
380
407
|
|
381
408
|
```ruby
|
382
409
|
iat = Time.now.to_i
|
383
|
-
iat_payload = { :
|
410
|
+
iat_payload = { data: 'data', iat: iat }
|
384
411
|
|
385
412
|
token = JWT.encode iat_payload, hmac_secret, 'HS256'
|
386
413
|
|
387
414
|
begin
|
388
415
|
# Add iat to the validation to check if the token has been manipulated
|
389
|
-
decoded_token = JWT.decode token, hmac_secret, true, { :
|
390
|
-
rescue JWT::InvalidIatError
|
391
|
-
# Handle invalid token, e.g. logout user or deny access
|
392
|
-
end
|
393
|
-
```
|
394
|
-
|
395
|
-
**Adding Leeway**
|
396
|
-
|
397
|
-
```ruby
|
398
|
-
iat = Time.now.to_i + 10
|
399
|
-
leeway = 30 # seconds
|
400
|
-
|
401
|
-
iat_payload = { :data => 'data', :iat => iat }
|
402
|
-
|
403
|
-
# build token issued in the future
|
404
|
-
token = JWT.encode iat_payload, hmac_secret, 'HS256'
|
405
|
-
|
406
|
-
begin
|
407
|
-
# add leeway to ensure the token is accepted
|
408
|
-
decoded_token = JWT.decode token, hmac_secret, true, { :iat_leeway => leeway, :verify_iat => true, :algorithm => 'HS256' }
|
416
|
+
decoded_token = JWT.decode token, hmac_secret, true, { verify_iat: true, algorithm: 'HS256' }
|
409
417
|
rescue JWT::InvalidIatError
|
410
418
|
# Handle invalid token, e.g. logout user or deny access
|
411
419
|
end
|
@@ -419,18 +427,43 @@ From [Oauth JSON Web Token 4.1.2. "sub" (Subject) Claim](https://tools.ietf.org/
|
|
419
427
|
|
420
428
|
```ruby
|
421
429
|
sub = 'Subject'
|
422
|
-
sub_payload = { :
|
430
|
+
sub_payload = { data: 'data', sub: sub }
|
423
431
|
|
424
432
|
token = JWT.encode sub_payload, hmac_secret, 'HS256'
|
425
433
|
|
426
434
|
begin
|
427
435
|
# Add sub to the validation to check if the token has been manipulated
|
428
|
-
decoded_token = JWT.decode token, hmac_secret, true, {
|
436
|
+
decoded_token = JWT.decode token, hmac_secret, true, { sub: sub, verify_sub: true, algorithm: 'HS256' }
|
429
437
|
rescue JWT::InvalidSubError
|
430
438
|
# Handle invalid token, e.g. logout user or deny access
|
431
439
|
end
|
432
440
|
```
|
433
441
|
|
442
|
+
### JSON Web Key (JWK)
|
443
|
+
|
444
|
+
JWK is a JSON structure representing a cryptographic key. Currently only supports RSA public keys.
|
445
|
+
|
446
|
+
```ruby
|
447
|
+
jwk = JWT::JWK.new(OpenSSL::PKey::RSA.new(2048))
|
448
|
+
payload, headers = { data: 'data' }, { kid: jwk.kid }
|
449
|
+
|
450
|
+
token = JWT.encode(payload, jwk.keypair, 'RS512', headers)
|
451
|
+
|
452
|
+
# The jwk loader would fetch the set of JWKs from a trusted source
|
453
|
+
jwk_loader = ->(options) do
|
454
|
+
@cached_keys = nil if options[:invalidate] # need to reload the keys
|
455
|
+
@cached_keys ||= { keys: [jwk.export] }
|
456
|
+
end
|
457
|
+
|
458
|
+
begin
|
459
|
+
JWT.decode(token, nil, true, { algorithms: ['RS512'], jwks: jwk_loader})
|
460
|
+
rescue JWT::JWKError
|
461
|
+
# Handle problems with the provided JWKs
|
462
|
+
rescue JWT::DecodeError
|
463
|
+
# Handle other decode related issues e.g. no kid in header, no matching public key found etc.
|
464
|
+
end
|
465
|
+
```
|
466
|
+
|
434
467
|
# Development and Tests
|
435
468
|
|
436
469
|
We depend on [Bundler](http://rubygems.org/gems/bundler) for defining gemspec and performing releases to rubygems.org, which can be done with
|
@@ -449,30 +482,8 @@ bundle exec rspec
|
|
449
482
|
|
450
483
|
## Contributors
|
451
484
|
|
452
|
-
|
453
|
-
* Ilya Zhitomirskiy <ilya@joindiaspora.com>
|
454
|
-
* Daniel Grippi <daniel@joindiaspora.com>
|
455
|
-
* Jeff Lindsay <progrium@gmail.com>
|
456
|
-
* Bob Aman <bob@sporkmonger.com>
|
457
|
-
* Micah Gates <github@mgates.com>
|
458
|
-
* Rob Wygand <rob@wygand.com>
|
459
|
-
* Ariel Salomon (Oscil8)
|
460
|
-
* Paul Battley <pbattley@gmail.com>
|
461
|
-
* Zane Shannon [@zshannon](https://github.com/zshannon)
|
462
|
-
* Brian Fletcher [@punkle](https://github.com/punkle)
|
463
|
-
* Alex [@ZhangHanDong](https://github.com/ZhangHanDong)
|
464
|
-
* John Downey [@jtdowney](https://github.com/jtdowney)
|
465
|
-
* Adam Greene [@skippy](https://github.com/skippy)
|
466
|
-
* Tim Rudat [@excpt](https://github.com/excpt) <timrudat@gmail.com> - Maintainer
|
485
|
+
See `AUTHORS` file.
|
467
486
|
|
468
487
|
## License
|
469
488
|
|
470
|
-
|
471
|
-
|
472
|
-
Copyright (c) 2011 Jeff Lindsay
|
473
|
-
|
474
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
475
|
-
|
476
|
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
477
|
-
|
478
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
489
|
+
See `LICENSE` file.
|