jwt 1.5.6 → 2.0.0.beta1
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.
- checksums.yaml +4 -4
 - data/.rubocop.yml +3 -0
 - data/.travis.yml +8 -8
 - data/CHANGELOG.md +48 -0
 - data/README.md +63 -7
 - data/lib/jwt.rb +31 -156
 - data/lib/jwt/decode.rb +13 -25
 - data/lib/jwt/default_options.rb +14 -0
 - data/lib/jwt/encode.rb +51 -0
 - data/lib/jwt/error.rb +1 -0
 - data/lib/jwt/signature.rb +145 -0
 - data/lib/jwt/verify.rb +31 -53
 - data/lib/jwt/version.rb +4 -4
 - data/ruby-jwt.gemspec +3 -2
 - data/spec/integration/readme_examples_spec.rb +17 -6
 - data/spec/jwt/verify_spec.rb +19 -26
 - data/spec/jwt_spec.rb +27 -34
 - data/spec/spec_helper.rb +3 -6
 - metadata +33 -18
 - data/lib/jwt/json.rb +0 -17
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: f874da696e03e2c7f0ffe02942540825eb8a6314
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: d57f6842df5cc35d21bea1a1e511026faeb0aaa4
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 51acfa10b330d62022d463cd4c0d25eff3ceb642b29e2c625d4aab40cd1a4138ca5597e0e19d540232f3f3b2e76fdcf4b403566f5b54aa7e687ab7d8696186e6
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: b8d9b8f1485f4e0c74d189c1e7ba64d71ce89f4813b134dbaf28263c695403dd102f12ab9dddde0ee7990f6cbcfde5ab5941876919bb1540b4088349129d1baf
         
     | 
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/.travis.yml
    CHANGED
    
    | 
         @@ -1,13 +1,13 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            sudo:  
     | 
| 
      
 1 
     | 
    
         
            +
            sudo: required
         
     | 
| 
       2 
2 
     | 
    
         
             
            cache: bundler
         
     | 
| 
       3 
3 
     | 
    
         
             
            language: ruby
         
     | 
| 
       4 
4 
     | 
    
         
             
            rvm:
         
     | 
| 
       5 
     | 
    
         
            -
              - 1.9.3
         
     | 
| 
       6 
     | 
    
         
            -
              - 2.0.0
         
     | 
| 
       7 
     | 
    
         
            -
              - 2.1.0
         
     | 
| 
       8 
5 
     | 
    
         
             
              - 2.2.0
         
     | 
| 
       9 
6 
     | 
    
         
             
              - 2.3.0
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
      
 7 
     | 
    
         
            +
              - 2.4.0
         
     | 
| 
      
 8 
     | 
    
         
            +
            script: "bundle exec rspec && bundle exec codeclimate-test-reporter"
         
     | 
| 
      
 9 
     | 
    
         
            +
            before_install:
         
     | 
| 
      
 10 
     | 
    
         
            +
              - sudo add-apt-repository ppa:chris-lea/libsodium -y
         
     | 
| 
      
 11 
     | 
    
         
            +
              - sudo apt-get update -q
         
     | 
| 
      
 12 
     | 
    
         
            +
              - sudo apt-get install libsodium-dev -y
         
     | 
| 
      
 13 
     | 
    
         
            +
              - gem install bundler
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,5 +1,52 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Change Log
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ## [v2.0.0](https://github.com/jwt/ruby-jwt/tree/v2.0.0) (2017-02-27)
         
     | 
| 
      
 4 
     | 
    
         
            +
            [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v1.5.6...v2.0.0)
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            **Implemented enhancements:**
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            - Error with method sign for String [\#171](https://github.com/jwt/ruby-jwt/issues/171)
         
     | 
| 
      
 9 
     | 
    
         
            +
            - Refactor the encondig code [\#121](https://github.com/jwt/ruby-jwt/issues/121)
         
     | 
| 
      
 10 
     | 
    
         
            +
            - Refactor [\#196](https://github.com/jwt/ruby-jwt/pull/196) ([EmilioCristalli](https://github.com/EmilioCristalli))
         
     | 
| 
      
 11 
     | 
    
         
            +
            - Move signature logic to its own module [\#195](https://github.com/jwt/ruby-jwt/pull/195) ([EmilioCristalli](https://github.com/EmilioCristalli))
         
     | 
| 
      
 12 
     | 
    
         
            +
            - Add options for claim-specific leeway [\#187](https://github.com/jwt/ruby-jwt/pull/187) ([EmilioCristalli](https://github.com/EmilioCristalli))
         
     | 
| 
      
 13 
     | 
    
         
            +
            - Add user friendly encode error if private key is a String, \#171 [\#176](https://github.com/jwt/ruby-jwt/pull/176) ([xamenrax](https://github.com/xamenrax))
         
     | 
| 
      
 14 
     | 
    
         
            +
            - Return empty string if signature less than byte\_size \#155 [\#175](https://github.com/jwt/ruby-jwt/pull/175) ([xamenrax](https://github.com/xamenrax))
         
     | 
| 
      
 15 
     | 
    
         
            +
            - Remove 'typ' optional parameter [\#174](https://github.com/jwt/ruby-jwt/pull/174) ([xamenrax](https://github.com/xamenrax))
         
     | 
| 
      
 16 
     | 
    
         
            +
            - Pass payload to keyfinder [\#172](https://github.com/jwt/ruby-jwt/pull/172) ([CodeMonkeySteve](https://github.com/CodeMonkeySteve))
         
     | 
| 
      
 17 
     | 
    
         
            +
            - Use RbNaCl for HMAC if available with fallback to OpenSSL [\#149](https://github.com/jwt/ruby-jwt/pull/149) ([mwpastore](https://github.com/mwpastore))
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            **Fixed bugs:**
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            - ruby-jwt::raw\_to\_asn1: Fails for signatures less than byte\_size [\#155](https://github.com/jwt/ruby-jwt/issues/155)
         
     | 
| 
      
 22 
     | 
    
         
            +
            - The leeway parameter is applies to all time based verifications [\#129](https://github.com/jwt/ruby-jwt/issues/129)
         
     | 
| 
      
 23 
     | 
    
         
            +
            - Add options for claim-specific leeway [\#187](https://github.com/jwt/ruby-jwt/pull/187) ([EmilioCristalli](https://github.com/EmilioCristalli))
         
     | 
| 
      
 24 
     | 
    
         
            +
            - Make algorithm option required to verify signature [\#184](https://github.com/jwt/ruby-jwt/pull/184) ([EmilioCristalli](https://github.com/EmilioCristalli))
         
     | 
| 
      
 25 
     | 
    
         
            +
            - Validate audience when payload is a scalar and options is an array [\#183](https://github.com/jwt/ruby-jwt/pull/183) ([steti](https://github.com/steti))
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            **Closed issues:**
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            - Different encoded value between servers with same password [\#197](https://github.com/jwt/ruby-jwt/issues/197)
         
     | 
| 
      
 30 
     | 
    
         
            +
            - Signature is different at each run [\#190](https://github.com/jwt/ruby-jwt/issues/190)
         
     | 
| 
      
 31 
     | 
    
         
            +
            - Include custom headers with password [\#189](https://github.com/jwt/ruby-jwt/issues/189)
         
     | 
| 
      
 32 
     | 
    
         
            +
            - can't create token - 'NotImplementedError: Unsupported signing method' [\#186](https://github.com/jwt/ruby-jwt/issues/186)
         
     | 
| 
      
 33 
     | 
    
         
            +
            - Why jwt depends on json \< 2.0 ? [\#179](https://github.com/jwt/ruby-jwt/issues/179)
         
     | 
| 
      
 34 
     | 
    
         
            +
            - Cannot verify JWT at all?? [\#177](https://github.com/jwt/ruby-jwt/issues/177)
         
     | 
| 
      
 35 
     | 
    
         
            +
            - verify\_iss: true is raising JWT::DecodeError instead of JWT::InvalidIssuerError [\#170](https://github.com/jwt/ruby-jwt/issues/170)
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            **Merged pull requests:**
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            - Add Codacy coverage reporter [\#194](https://github.com/jwt/ruby-jwt/pull/194) ([excpt](https://github.com/excpt))
         
     | 
| 
      
 40 
     | 
    
         
            +
            - Add minimum required ruby version to gemspec [\#193](https://github.com/jwt/ruby-jwt/pull/193) ([excpt](https://github.com/excpt))
         
     | 
| 
      
 41 
     | 
    
         
            +
            - Code smell fixes [\#192](https://github.com/jwt/ruby-jwt/pull/192) ([excpt](https://github.com/excpt))
         
     | 
| 
      
 42 
     | 
    
         
            +
            - Version bump to 2.0.0.dev [\#191](https://github.com/jwt/ruby-jwt/pull/191) ([excpt](https://github.com/excpt))
         
     | 
| 
      
 43 
     | 
    
         
            +
            - Basic encode module refactoring \#121 [\#182](https://github.com/jwt/ruby-jwt/pull/182) ([xamenrax](https://github.com/xamenrax))
         
     | 
| 
      
 44 
     | 
    
         
            +
            - Fix travis ci build configuration [\#181](https://github.com/jwt/ruby-jwt/pull/181) ([excpt](https://github.com/excpt))
         
     | 
| 
      
 45 
     | 
    
         
            +
            - Fix travis ci build configuration [\#180](https://github.com/jwt/ruby-jwt/pull/180) ([excpt](https://github.com/excpt))
         
     | 
| 
      
 46 
     | 
    
         
            +
            - Fix typo in README [\#178](https://github.com/jwt/ruby-jwt/pull/178) ([tomeduarte](https://github.com/tomeduarte))
         
     | 
| 
      
 47 
     | 
    
         
            +
            - Fix code style [\#173](https://github.com/jwt/ruby-jwt/pull/173) ([excpt](https://github.com/excpt))
         
     | 
| 
      
 48 
     | 
    
         
            +
            - Fixed a typo in a spec name [\#169](https://github.com/jwt/ruby-jwt/pull/169) ([Mingan](https://github.com/Mingan))
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
       3 
50 
     | 
    
         
             
            ## [v1.5.6](https://github.com/jwt/ruby-jwt/tree/v1.5.6) (2016-09-19)
         
     | 
| 
       4 
51 
     | 
    
         
             
            [Full Changelog](https://github.com/jwt/ruby-jwt/compare/v1.5.5...v1.5.6)
         
     | 
| 
       5 
52 
     | 
    
         | 
| 
         @@ -9,6 +56,7 @@ 
     | 
|
| 
       9 
56 
     | 
    
         | 
| 
       10 
57 
     | 
    
         
             
            **Merged pull requests:**
         
     | 
| 
       11 
58 
     | 
    
         | 
| 
      
 59 
     | 
    
         
            +
            - Update changelog [\#168](https://github.com/jwt/ruby-jwt/pull/168) ([excpt](https://github.com/excpt))
         
     | 
| 
       12 
60 
     | 
    
         
             
            - Fix rubocop code smells [\#167](https://github.com/jwt/ruby-jwt/pull/167) ([excpt](https://github.com/excpt))
         
     | 
| 
       13 
61 
     | 
    
         | 
| 
       14 
62 
     | 
    
         
             
            ## [v1.5.5](https://github.com/jwt/ruby-jwt/tree/v1.5.5) (2016-09-16)
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -7,7 +7,7 @@ 
     | 
|
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            A pure ruby implementation of the [RFC 7519 OAuth JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519) standard.
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
            If you have further questions  
     | 
| 
      
 10 
     | 
    
         
            +
            If you have further questions related to development or usage, join us: [ruby-jwt google group](https://groups.google.com/forum/#!forum/ruby-jwt).
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
            ## Announcements
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
         @@ -55,7 +55,7 @@ decoded_token = JWT.decode token, nil, false 
     | 
|
| 
       55 
55 
     | 
    
         
             
            # Array
         
     | 
| 
       56 
56 
     | 
    
         
             
            # [
         
     | 
| 
       57 
57 
     | 
    
         
             
            #   {"data"=>"test"}, # payload
         
     | 
| 
       58 
     | 
    
         
            -
            #   {" 
     | 
| 
      
 58 
     | 
    
         
            +
            #   {"alg"=>"none"} # header
         
     | 
| 
       59 
59 
     | 
    
         
             
            # ]
         
     | 
| 
       60 
60 
     | 
    
         
             
            puts decoded_token
         
     | 
| 
       61 
61 
     | 
    
         
             
            ```
         
     | 
| 
         @@ -63,6 +63,7 @@ puts decoded_token 
     | 
|
| 
       63 
63 
     | 
    
         
             
            **HMAC** (default: HS256)
         
     | 
| 
       64 
64 
     | 
    
         | 
| 
       65 
65 
     | 
    
         
             
            * HS256 - HMAC using SHA-256 hash algorithm (default)
         
     | 
| 
      
 66 
     | 
    
         
            +
            * HS512256 - HMAC using SHA-512/256 hash algorithm (only available with RbNaCl; see note below)
         
     | 
| 
       66 
67 
     | 
    
         
             
            * HS384 - HMAC using SHA-384 hash algorithm
         
     | 
| 
       67 
68 
     | 
    
         
             
            * HS512 - HMAC using SHA-512 hash algorithm
         
     | 
| 
       68 
69 
     | 
    
         | 
| 
         @@ -79,11 +80,13 @@ decoded_token = JWT.decode token, hmac_secret, true, { :algorithm => 'HS256' } 
     | 
|
| 
       79 
80 
     | 
    
         
             
            # Array
         
     | 
| 
       80 
81 
     | 
    
         
             
            # [
         
     | 
| 
       81 
82 
     | 
    
         
             
            #   {"data"=>"test"}, # payload
         
     | 
| 
       82 
     | 
    
         
            -
            #   {" 
     | 
| 
      
 83 
     | 
    
         
            +
            #   {"alg"=>"HS256"} # header
         
     | 
| 
       83 
84 
     | 
    
         
             
            # ]
         
     | 
| 
       84 
85 
     | 
    
         
             
            puts decoded_token
         
     | 
| 
       85 
86 
     | 
    
         
             
            ```
         
     | 
| 
       86 
87 
     | 
    
         | 
| 
      
 88 
     | 
    
         
            +
            Note: If [RbNaCl](https://github.com/cryptosphere/rbnacl) is loadable, ruby-jwt will use it for HMAC-SHA256, HMAC-SHA512/256, and HMAC-SHA512. RbNaCl enforces a maximum key size of 32 bytes for these algorithms.
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
       87 
90 
     | 
    
         
             
            **RSA**
         
     | 
| 
       88 
91 
     | 
    
         | 
| 
       89 
92 
     | 
    
         
             
            * RS256 - RSA using SHA-256 hash algorithm
         
     | 
| 
         @@ -104,7 +107,7 @@ decoded_token = JWT.decode token, rsa_public, true, { :algorithm => 'RS256' } 
     | 
|
| 
       104 
107 
     | 
    
         
             
            # Array
         
     | 
| 
       105 
108 
     | 
    
         
             
            # [
         
     | 
| 
       106 
109 
     | 
    
         
             
            #   {"data"=>"test"}, # payload
         
     | 
| 
       107 
     | 
    
         
            -
            #   {" 
     | 
| 
      
 110 
     | 
    
         
            +
            #   {"alg"=>"RS256"} # header
         
     | 
| 
       108 
111 
     | 
    
         
             
            # ]
         
     | 
| 
       109 
112 
     | 
    
         
             
            puts decoded_token
         
     | 
| 
       110 
113 
     | 
    
         
             
            ```
         
     | 
| 
         @@ -131,7 +134,7 @@ decoded_token = JWT.decode token, ecdsa_public, true, { :algorithm => 'ES256' } 
     | 
|
| 
       131 
134 
     | 
    
         
             
            # Array
         
     | 
| 
       132 
135 
     | 
    
         
             
            # [
         
     | 
| 
       133 
136 
     | 
    
         
             
            #    {"test"=>"data"}, # payload
         
     | 
| 
       134 
     | 
    
         
            -
            #    {" 
     | 
| 
      
 137 
     | 
    
         
            +
            #    {"alg"=>"ES256"} # header
         
     | 
| 
       135 
138 
     | 
    
         
             
            # ]
         
     | 
| 
       136 
139 
     | 
    
         
             
            puts decoded_token
         
     | 
| 
       137 
140 
     | 
    
         
             
            ```
         
     | 
| 
         @@ -152,6 +155,38 @@ used. JWT supports these reserved claim names: 
     | 
|
| 
       152 
155 
     | 
    
         
             
             - 'iat' (Issued At) Claim
         
     | 
| 
       153 
156 
     | 
    
         
             
             - 'sub' (Subject) Claim
         
     | 
| 
       154 
157 
     | 
    
         | 
| 
      
 158 
     | 
    
         
            +
            ## Add custom header fields
         
     | 
| 
      
 159 
     | 
    
         
            +
            Ruby-jwt gem supports custom [header fields] (https://tools.ietf.org/html/rfc7519#section-5)
         
     | 
| 
      
 160 
     | 
    
         
            +
            To add custom header fields you need to pass `header_fields` parameter
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 163 
     | 
    
         
            +
            token = JWT.encode payload, key, algorithm='HS256', header_fields={}
         
     | 
| 
      
 164 
     | 
    
         
            +
            ```
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
            **Example:**
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 169 
     | 
    
         
            +
            require 'jwt'
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
            payload = {:data => 'test'}
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
            # IMPORTANT: set nil as password parameter
         
     | 
| 
      
 174 
     | 
    
         
            +
            token = JWT.encode payload, nil, 'none', { :typ => "JWT" }
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
            # eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJkYXRhIjoidGVzdCJ9.
         
     | 
| 
      
 177 
     | 
    
         
            +
            puts token
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
            # Set password to nil and validation to false otherwise this won't work
         
     | 
| 
      
 180 
     | 
    
         
            +
            decoded_token = JWT.decode token, nil, false
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
            # Array
         
     | 
| 
      
 183 
     | 
    
         
            +
            # [
         
     | 
| 
      
 184 
     | 
    
         
            +
            #   {"data"=>"test"}, # payload
         
     | 
| 
      
 185 
     | 
    
         
            +
            #   {"typ"=>"JWT", "alg"=>"none"} # header
         
     | 
| 
      
 186 
     | 
    
         
            +
            # ]
         
     | 
| 
      
 187 
     | 
    
         
            +
            puts decoded_token
         
     | 
| 
      
 188 
     | 
    
         
            +
            ```
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
       155 
190 
     | 
    
         
             
            ### Expiration Time Claim
         
     | 
| 
       156 
191 
     | 
    
         | 
| 
       157 
192 
     | 
    
         
             
            From [Oauth JSON Web Token 4.1.4. "exp" (Expiration Time) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.4):
         
     | 
| 
         @@ -186,7 +221,7 @@ token = JWT.encode exp_payload, hmac_secret, 'HS256' 
     | 
|
| 
       186 
221 
     | 
    
         | 
| 
       187 
222 
     | 
    
         
             
            begin
         
     | 
| 
       188 
223 
     | 
    
         
             
              # add leeway to ensure the token is still accepted
         
     | 
| 
       189 
     | 
    
         
            -
              decoded_token = JWT.decode token, hmac_secret, true, { : 
     | 
| 
      
 224 
     | 
    
         
            +
              decoded_token = JWT.decode token, hmac_secret, true, { :exp_leeway => leeway, :algorithm => 'HS256' }
         
     | 
| 
       190 
225 
     | 
    
         
             
            rescue JWT::ExpiredSignature
         
     | 
| 
       191 
226 
     | 
    
         
             
              # Handle expired token, e.g. logout user or deny access
         
     | 
| 
       192 
227 
     | 
    
         
             
            end
         
     | 
| 
         @@ -226,7 +261,7 @@ token = JWT.encode nbf_payload, hmac_secret, 'HS256' 
     | 
|
| 
       226 
261 
     | 
    
         | 
| 
       227 
262 
     | 
    
         
             
            begin
         
     | 
| 
       228 
263 
     | 
    
         
             
              # add leeway to ensure the token is valid
         
     | 
| 
       229 
     | 
    
         
            -
              decoded_token = JWT.decode token, hmac_secret, true, { : 
     | 
| 
      
 264 
     | 
    
         
            +
              decoded_token = JWT.decode token, hmac_secret, true, { :nbf_leeway => leeway, :algorithm => 'HS256' }
         
     | 
| 
       230 
265 
     | 
    
         
             
            rescue JWT::ImmatureSignature
         
     | 
| 
       231 
266 
     | 
    
         
             
              # Handle invalid token, e.g. logout user or deny access
         
     | 
| 
       232 
267 
     | 
    
         
             
            end
         
     | 
| 
         @@ -305,6 +340,8 @@ From [Oauth JSON Web Token 4.1.6. "iat" (Issued At) Claim](https://tools.ietf.or 
     | 
|
| 
       305 
340 
     | 
    
         | 
| 
       306 
341 
     | 
    
         
             
            > 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.
         
     | 
| 
       307 
342 
     | 
    
         | 
| 
      
 343 
     | 
    
         
            +
            **Handle Issued At Claim**
         
     | 
| 
      
 344 
     | 
    
         
            +
             
     | 
| 
       308 
345 
     | 
    
         
             
            ```ruby
         
     | 
| 
       309 
346 
     | 
    
         
             
            iat = Time.now.to_i
         
     | 
| 
       310 
347 
     | 
    
         
             
            iat_payload = { :data => 'data', :iat => iat }
         
     | 
| 
         @@ -319,6 +356,25 @@ rescue JWT::InvalidIatError 
     | 
|
| 
       319 
356 
     | 
    
         
             
            end
         
     | 
| 
       320 
357 
     | 
    
         
             
            ```
         
     | 
| 
       321 
358 
     | 
    
         | 
| 
      
 359 
     | 
    
         
            +
            **Adding Leeway**
         
     | 
| 
      
 360 
     | 
    
         
            +
             
     | 
| 
      
 361 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 362 
     | 
    
         
            +
            iat = Time.now.to_i + 10
         
     | 
| 
      
 363 
     | 
    
         
            +
            leeway = 30 # seconds
         
     | 
| 
      
 364 
     | 
    
         
            +
             
     | 
| 
      
 365 
     | 
    
         
            +
            iat_payload = { :data => 'data', :iat => iat }
         
     | 
| 
      
 366 
     | 
    
         
            +
             
     | 
| 
      
 367 
     | 
    
         
            +
            # build token issued in the future
         
     | 
| 
      
 368 
     | 
    
         
            +
            token = JWT.encode iat_payload, hmac_secret, 'HS256'
         
     | 
| 
      
 369 
     | 
    
         
            +
             
     | 
| 
      
 370 
     | 
    
         
            +
            begin
         
     | 
| 
      
 371 
     | 
    
         
            +
              # add leeway to ensure the token is accepted
         
     | 
| 
      
 372 
     | 
    
         
            +
              decoded_token = JWT.decode token, hmac_secret, true, { :iat_leeway => leeway, :verify_iat => true, :algorithm => 'HS256' }
         
     | 
| 
      
 373 
     | 
    
         
            +
            rescue JWT::InvalidIatError
         
     | 
| 
      
 374 
     | 
    
         
            +
              # Handle invalid token, e.g. logout user or deny access
         
     | 
| 
      
 375 
     | 
    
         
            +
            end
         
     | 
| 
      
 376 
     | 
    
         
            +
            ```
         
     | 
| 
      
 377 
     | 
    
         
            +
             
     | 
| 
       322 
378 
     | 
    
         
             
            ### Subject Claim
         
     | 
| 
       323 
379 
     | 
    
         | 
| 
       324 
380 
     | 
    
         
             
            From [Oauth JSON Web Token 4.1.2. "sub" (Subject) Claim](https://tools.ietf.org/html/rfc7519#section-4.1.2):
         
     | 
    
        data/lib/jwt.rb
    CHANGED
    
    | 
         @@ -1,192 +1,67 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         
             
            require 'base64'
         
     | 
| 
       3 
     | 
    
         
            -
            require 'openssl'
         
     | 
| 
       4 
3 
     | 
    
         
             
            require 'jwt/decode'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'jwt/default_options'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'jwt/encode'
         
     | 
| 
       5 
6 
     | 
    
         
             
            require 'jwt/error'
         
     | 
| 
       6 
     | 
    
         
            -
            require 'jwt/ 
     | 
| 
      
 7 
     | 
    
         
            +
            require 'jwt/signature'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'jwt/verify'
         
     | 
| 
       7 
9 
     | 
    
         | 
| 
       8 
10 
     | 
    
         
             
            # JSON Web Token implementation
         
     | 
| 
       9 
11 
     | 
    
         
             
            #
         
     | 
| 
       10 
12 
     | 
    
         
             
            # Should be up to date with the latest spec:
         
     | 
| 
       11 
     | 
    
         
            -
            # https://tools.ietf.org/html/rfc7519 
     | 
| 
      
 13 
     | 
    
         
            +
            # https://tools.ietf.org/html/rfc7519
         
     | 
| 
       12 
14 
     | 
    
         
             
            module JWT
         
     | 
| 
       13 
     | 
    
         
            -
               
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
              NAMED_CURVES = {
         
     | 
| 
       16 
     | 
    
         
            -
                'prime256v1' => 'ES256',
         
     | 
| 
       17 
     | 
    
         
            -
                'secp384r1' => 'ES384',
         
     | 
| 
       18 
     | 
    
         
            -
                'secp521r1' => 'ES512'
         
     | 
| 
       19 
     | 
    
         
            -
              }.freeze
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
              DEFAULT_OPTIONS = {
         
     | 
| 
       22 
     | 
    
         
            -
                verify_expiration: true,
         
     | 
| 
       23 
     | 
    
         
            -
                verify_not_before: true,
         
     | 
| 
       24 
     | 
    
         
            -
                verify_iss: false,
         
     | 
| 
       25 
     | 
    
         
            -
                verify_iat: false,
         
     | 
| 
       26 
     | 
    
         
            -
                verify_jti: false,
         
     | 
| 
       27 
     | 
    
         
            -
                verify_aud: false,
         
     | 
| 
       28 
     | 
    
         
            -
                verify_sub: false,
         
     | 
| 
       29 
     | 
    
         
            -
                leeway: 0
         
     | 
| 
       30 
     | 
    
         
            -
              }.freeze
         
     | 
| 
      
 15 
     | 
    
         
            +
              include JWT::DefaultOptions
         
     | 
| 
       31 
16 
     | 
    
         | 
| 
       32 
17 
     | 
    
         
             
              module_function
         
     | 
| 
       33 
18 
     | 
    
         | 
| 
       34 
     | 
    
         
            -
              def  
     | 
| 
       35 
     | 
    
         
            -
                 
     | 
| 
       36 
     | 
    
         
            -
                  sign_hmac(algorithm, msg, key)
         
     | 
| 
       37 
     | 
    
         
            -
                elsif %w(RS256 RS384 RS512).include?(algorithm)
         
     | 
| 
       38 
     | 
    
         
            -
                  sign_rsa(algorithm, msg, key)
         
     | 
| 
       39 
     | 
    
         
            -
                elsif %w(ES256 ES384 ES512).include?(algorithm)
         
     | 
| 
       40 
     | 
    
         
            -
                  sign_ecdsa(algorithm, msg, key)
         
     | 
| 
       41 
     | 
    
         
            -
                else
         
     | 
| 
       42 
     | 
    
         
            -
                  raise NotImplementedError, 'Unsupported signing method'
         
     | 
| 
       43 
     | 
    
         
            -
                end
         
     | 
| 
       44 
     | 
    
         
            -
              end
         
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
     | 
    
         
            -
              def sign_rsa(algorithm, msg, private_key)
         
     | 
| 
       47 
     | 
    
         
            -
                private_key.sign(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), msg)
         
     | 
| 
       48 
     | 
    
         
            -
              end
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
              def sign_ecdsa(algorithm, msg, private_key)
         
     | 
| 
       51 
     | 
    
         
            -
                key_algorithm = NAMED_CURVES[private_key.group.curve_name]
         
     | 
| 
       52 
     | 
    
         
            -
                if algorithm != key_algorithm
         
     | 
| 
       53 
     | 
    
         
            -
                  raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} signing key was provided"
         
     | 
| 
       54 
     | 
    
         
            -
                end
         
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
                digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
         
     | 
| 
       57 
     | 
    
         
            -
                asn1_to_raw(private_key.dsa_sign_asn1(digest.digest(msg)), private_key)
         
     | 
| 
       58 
     | 
    
         
            -
              end
         
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
              def verify_rsa(algorithm, public_key, signing_input, signature)
         
     | 
| 
       61 
     | 
    
         
            -
                public_key.verify(OpenSSL::Digest.new(algorithm.sub('RS', 'sha')), signature, signing_input)
         
     | 
| 
       62 
     | 
    
         
            -
              end
         
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
              def verify_ecdsa(algorithm, public_key, signing_input, signature)
         
     | 
| 
       65 
     | 
    
         
            -
                key_algorithm = NAMED_CURVES[public_key.group.curve_name]
         
     | 
| 
       66 
     | 
    
         
            -
                if algorithm != key_algorithm
         
     | 
| 
       67 
     | 
    
         
            -
                  raise IncorrectAlgorithm, "payload algorithm is #{algorithm} but #{key_algorithm} verification key was provided"
         
     | 
| 
       68 
     | 
    
         
            -
                end
         
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
                digest = OpenSSL::Digest.new(algorithm.sub('ES', 'sha'))
         
     | 
| 
       71 
     | 
    
         
            -
                public_key.dsa_verify_asn1(digest.digest(signing_input), raw_to_asn1(signature, public_key))
         
     | 
| 
       72 
     | 
    
         
            -
              end
         
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
              def sign_hmac(algorithm, msg, key)
         
     | 
| 
       75 
     | 
    
         
            -
                OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub('HS', 'sha')), key, msg)
         
     | 
| 
       76 
     | 
    
         
            -
              end
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
              def base64url_encode(str)
         
     | 
| 
       79 
     | 
    
         
            -
                Base64.encode64(str).tr('+/', '-_').gsub(/[\n=]/, '')
         
     | 
| 
       80 
     | 
    
         
            -
              end
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
              def encoded_header(algorithm = 'HS256', header_fields = {})
         
     | 
| 
       83 
     | 
    
         
            -
                header = { 'typ' => 'JWT', 'alg' => algorithm }.merge(header_fields)
         
     | 
| 
       84 
     | 
    
         
            -
                base64url_encode(encode_json(header))
         
     | 
| 
       85 
     | 
    
         
            -
              end
         
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
              def encoded_payload(payload)
         
     | 
| 
       88 
     | 
    
         
            -
                raise InvalidPayload, 'exp claim must be an integer' if payload['exp'] && payload['exp'].is_a?(Time)
         
     | 
| 
       89 
     | 
    
         
            -
                base64url_encode(encode_json(payload))
         
     | 
| 
       90 
     | 
    
         
            -
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
              def decoded_segments(jwt, verify = true)
         
     | 
| 
      
 20 
     | 
    
         
            +
                raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
         
     | 
| 
       91 
21 
     | 
    
         | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
                 
     | 
| 
       94 
     | 
    
         
            -
                  ''
         
     | 
| 
       95 
     | 
    
         
            -
                else
         
     | 
| 
       96 
     | 
    
         
            -
                  signature = sign(algorithm, signing_input, key)
         
     | 
| 
       97 
     | 
    
         
            -
                  base64url_encode(signature)
         
     | 
| 
       98 
     | 
    
         
            -
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
                decoder = Decode.new jwt, verify
         
     | 
| 
      
 23 
     | 
    
         
            +
                decoder.decode_segments
         
     | 
| 
       99 
24 
     | 
    
         
             
              end
         
     | 
| 
       100 
25 
     | 
    
         | 
| 
       101 
26 
     | 
    
         
             
              def encode(payload, key, algorithm = 'HS256', header_fields = {})
         
     | 
| 
       102 
     | 
    
         
            -
                algorithm  
     | 
| 
       103 
     | 
    
         
            -
                segments 
     | 
| 
       104 
     | 
    
         
            -
                segments << encoded_header(algorithm, header_fields)
         
     | 
| 
       105 
     | 
    
         
            -
                segments << encoded_payload(payload)
         
     | 
| 
       106 
     | 
    
         
            -
                segments << encoded_signature(segments.join('.'), key, algorithm)
         
     | 
| 
       107 
     | 
    
         
            -
                segments.join('.')
         
     | 
| 
       108 
     | 
    
         
            -
              end
         
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
     | 
    
         
            -
              def decoded_segments(jwt, key = nil, verify = true, custom_options = {}, &keyfinder)
         
     | 
| 
       111 
     | 
    
         
            -
                raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
         
     | 
| 
       112 
     | 
    
         
            -
             
     | 
| 
       113 
     | 
    
         
            -
                merged_options = DEFAULT_OPTIONS.merge(custom_options)
         
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                decoder = Decode.new jwt, key, verify, merged_options, &keyfinder
         
     | 
| 
       116 
     | 
    
         
            -
                decoder.decode_segments
         
     | 
| 
      
 27 
     | 
    
         
            +
                encoder = Encode.new payload, key, algorithm, header_fields
         
     | 
| 
      
 28 
     | 
    
         
            +
                encoder.segments
         
     | 
| 
       117 
29 
     | 
    
         
             
              end
         
     | 
| 
       118 
30 
     | 
    
         | 
| 
       119 
31 
     | 
    
         
             
              def decode(jwt, key = nil, verify = true, custom_options = {}, &keyfinder)
         
     | 
| 
       120 
32 
     | 
    
         
             
                raise(JWT::DecodeError, 'Nil JSON web token') unless jwt
         
     | 
| 
       121 
33 
     | 
    
         | 
| 
       122 
34 
     | 
    
         
             
                merged_options = DEFAULT_OPTIONS.merge(custom_options)
         
     | 
| 
       123 
     | 
    
         
            -
             
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                decoder = Decode.new jwt, verify
         
     | 
| 
       124 
37 
     | 
    
         
             
                header, payload, signature, signing_input = decoder.decode_segments
         
     | 
| 
       125 
     | 
    
         
            -
                decode_verify_signature(key, header, signature, signing_input, merged_options, &keyfinder) if verify
         
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
      
 38 
     | 
    
         
            +
                decode_verify_signature(key, header, payload, signature, signing_input, merged_options, &keyfinder) if verify
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                Verify.verify_claims(payload, merged_options)
         
     | 
| 
       127 
41 
     | 
    
         | 
| 
       128 
42 
     | 
    
         
             
                raise(JWT::DecodeError, 'Not enough or too many segments') unless header && payload
         
     | 
| 
       129 
43 
     | 
    
         | 
| 
       130 
44 
     | 
    
         
             
                [payload, header]
         
     | 
| 
       131 
45 
     | 
    
         
             
              end
         
     | 
| 
       132 
46 
     | 
    
         | 
| 
       133 
     | 
    
         
            -
              def decode_verify_signature(key, header, signature, signing_input, options, &keyfinder)
         
     | 
| 
       134 
     | 
    
         
            -
                algo, key = signature_algorithm_and_key(header, key, &keyfinder)
         
     | 
| 
       135 
     | 
    
         
            -
                if options[:algorithm] && algo != options[:algorithm]
         
     | 
| 
       136 
     | 
    
         
            -
                  raise JWT::IncorrectAlgorithm, 'Expected a different algorithm'
         
     | 
| 
       137 
     | 
    
         
            -
                end
         
     | 
| 
       138 
     | 
    
         
            -
                verify_signature(algo, key, signing_input, signature)
         
     | 
| 
       139 
     | 
    
         
            -
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
              def decode_verify_signature(key, header, payload, signature, signing_input, options, &keyfinder)
         
     | 
| 
      
 48 
     | 
    
         
            +
                algo, key = signature_algorithm_and_key(header, payload, key, &keyfinder)
         
     | 
| 
       140 
49 
     | 
    
         | 
| 
       141 
     | 
    
         
            -
             
     | 
| 
       142 
     | 
    
         
            -
                 
     | 
| 
       143 
     | 
    
         
            -
                [header['alg'], key]
         
     | 
| 
       144 
     | 
    
         
            -
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
                raise(JWT::IncorrectAlgorithm, 'An algorithm must be specified') unless options[:algorithm]
         
     | 
| 
      
 51 
     | 
    
         
            +
                raise(JWT::IncorrectAlgorithm, 'Expected a different algorithm') unless algo == options[:algorithm]
         
     | 
| 
       145 
52 
     | 
    
         | 
| 
       146 
     | 
    
         
            -
             
     | 
| 
       147 
     | 
    
         
            -
                verify_signature_algo(algo, key, signing_input, signature)
         
     | 
| 
       148 
     | 
    
         
            -
              rescue OpenSSL::PKey::PKeyError
         
     | 
| 
       149 
     | 
    
         
            -
                raise JWT::VerificationError, 'Signature verification raised'
         
     | 
| 
       150 
     | 
    
         
            -
              ensure
         
     | 
| 
       151 
     | 
    
         
            -
                OpenSSL.errors.clear
         
     | 
| 
      
 53 
     | 
    
         
            +
                Signature.verify(algo, key, signing_input, signature)
         
     | 
| 
       152 
54 
     | 
    
         
             
              end
         
     | 
| 
       153 
55 
     | 
    
         | 
| 
       154 
     | 
    
         
            -
              def  
     | 
| 
       155 
     | 
    
         
            -
                if  
     | 
| 
       156 
     | 
    
         
            -
                   
     | 
| 
       157 
     | 
    
         
            -
             
     | 
| 
       158 
     | 
    
         
            -
             
     | 
| 
       159 
     | 
    
         
            -
             
     | 
| 
       160 
     | 
    
         
            -
             
     | 
| 
       161 
     | 
    
         
            -
             
     | 
| 
       162 
     | 
    
         
            -
                  raise JWT::VerificationError, 'Algorithm not supported'
         
     | 
| 
      
 56 
     | 
    
         
            +
              def signature_algorithm_and_key(header, payload, key, &keyfinder)
         
     | 
| 
      
 57 
     | 
    
         
            +
                if keyfinder
         
     | 
| 
      
 58 
     | 
    
         
            +
                  key = if keyfinder.arity == 2
         
     | 
| 
      
 59 
     | 
    
         
            +
                          yield(header, payload)
         
     | 
| 
      
 60 
     | 
    
         
            +
                        else
         
     | 
| 
      
 61 
     | 
    
         
            +
                          yield(header)
         
     | 
| 
      
 62 
     | 
    
         
            +
                        end
         
     | 
| 
      
 63 
     | 
    
         
            +
                  raise JWT::DecodeError, 'No verification key available' unless key
         
     | 
| 
       163 
64 
     | 
    
         
             
                end
         
     | 
| 
       164 
     | 
    
         
            -
             
     | 
| 
       165 
     | 
    
         
            -
             
     | 
| 
       166 
     | 
    
         
            -
              # From devise
         
     | 
| 
       167 
     | 
    
         
            -
              # constant-time comparison algorithm to prevent timing attacks
         
     | 
| 
       168 
     | 
    
         
            -
              def secure_compare(a, b)
         
     | 
| 
       169 
     | 
    
         
            -
                return false if a.nil? || b.nil? || a.empty? || b.empty? || a.bytesize != b.bytesize
         
     | 
| 
       170 
     | 
    
         
            -
                l = a.unpack "C#{a.bytesize}"
         
     | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
       172 
     | 
    
         
            -
                res = 0
         
     | 
| 
       173 
     | 
    
         
            -
                b.each_byte { |byte| res |= byte ^ l.shift }
         
     | 
| 
       174 
     | 
    
         
            -
                res.zero?
         
     | 
| 
       175 
     | 
    
         
            -
              end
         
     | 
| 
       176 
     | 
    
         
            -
             
     | 
| 
       177 
     | 
    
         
            -
              def raw_to_asn1(signature, private_key)
         
     | 
| 
       178 
     | 
    
         
            -
                byte_size = (private_key.group.degree + 7) / 8
         
     | 
| 
       179 
     | 
    
         
            -
                r = signature[0..(byte_size - 1)]
         
     | 
| 
       180 
     | 
    
         
            -
                s = signature[byte_size..-1]
         
     | 
| 
       181 
     | 
    
         
            -
                OpenSSL::ASN1::Sequence.new([r, s].map { |int| OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(int, 2)) }).to_der
         
     | 
| 
       182 
     | 
    
         
            -
              end
         
     | 
| 
       183 
     | 
    
         
            -
             
     | 
| 
       184 
     | 
    
         
            -
              def asn1_to_raw(signature, public_key)
         
     | 
| 
       185 
     | 
    
         
            -
                byte_size = (public_key.group.degree + 7) / 8
         
     | 
| 
       186 
     | 
    
         
            -
                OpenSSL::ASN1.decode(signature).value.map { |value| value.value.to_s(2).rjust(byte_size, "\x00") }.join
         
     | 
| 
       187 
     | 
    
         
            -
              end
         
     | 
| 
       188 
     | 
    
         
            -
             
     | 
| 
       189 
     | 
    
         
            -
              def base64url_decode(str)
         
     | 
| 
       190 
     | 
    
         
            -
                Decode.base64url_decode(str)
         
     | 
| 
      
 65 
     | 
    
         
            +
                [header['alg'], key]
         
     | 
| 
       191 
66 
     | 
    
         
             
              end
         
     | 
| 
       192 
67 
     | 
    
         
             
            end
         
     |