jwt_sessions 2.3.1 → 2.4.0
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/README.md +82 -28
- data/lib/jwt_sessions.rb +8 -0
- data/lib/jwt_sessions/access_token.rb +2 -2
- data/lib/jwt_sessions/refresh_token.rb +12 -4
- data/lib/jwt_sessions/session.rb +17 -7
- data/lib/jwt_sessions/version.rb +1 -1
- data/test/units/jwt_sessions/test_session.rb +24 -0
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 8fe66c91df4011e6e59d05bda8a9d43188b8f07a
         | 
| 4 | 
            +
              data.tar.gz: 97bb42e7a8f6ecb5037b3bc6b057758dfcf2397d
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: a353fc811d0cd645655467088651f33b22e9dde3778a8fe2ef027bb07f5274a98acb0f9243a8791251d203e14445f453e2ee61af6829b078e4150f199a0332cc
         | 
| 7 | 
            +
              data.tar.gz: baef0f6687c8ec9171e67a74fda5d5f4907a600e7d6ce829a5d01e39cfb62af6777dce235bcfcc989313c6d9b3273abbd914da8d0a074b9f0ceff517398f87e1
         | 
    
        data/README.md
    CHANGED
    
    | @@ -11,6 +11,7 @@ XSS/CSRF safe JWT auth designed for SPA | |
| 11 11 | 
             
            - [Synopsis](#synopsis)
         | 
| 12 12 | 
             
            - [Installation](#installation)
         | 
| 13 13 | 
             
            - [Getting Started](#getting-started)
         | 
| 14 | 
            +
              * [Creating a session](#creating-a-session)
         | 
| 14 15 | 
             
              * [Rails integration](#rails-integration)
         | 
| 15 16 | 
             
              * [Non-Rails usage](#non-rails-usage)
         | 
| 16 17 | 
             
            - [Configuration](#configuration)
         | 
| @@ -32,7 +33,9 @@ XSS/CSRF safe JWT auth designed for SPA | |
| 32 33 |  | 
| 33 34 | 
             
            Main goal of this gem is to provide configurable, manageable, and safe stateful sessions based on JSON Web Tokens.
         | 
| 34 35 |  | 
| 35 | 
            -
             | 
| 36 | 
            +
            The gem stores JWT based sessions on the backend (currently, redis and memory stores are supported), making it possible to manage sessions, reset passwords, logout users in a reliable and secure way.
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            It's designed to be framework agnostic yet is easily integrable, and Rails integration is available out of the box.
         | 
| 36 39 |  | 
| 37 40 | 
             
            Core concept behind `jwt_sessions` is that each session is represented by a pair of tokens: access and refresh, and a session store is used to handle CSRF checks and refresh token hijacking. Both tokens have configurable expiration times, but in general refresh token is supposed to have a longer lifespan than an access token. Access token is used to retrieve secured resources and refresh token is used to renew the access token once it's expired. Default token store is based on redis.
         | 
| 38 41 |  | 
| @@ -56,7 +59,80 @@ bundle install | |
| 56 59 |  | 
| 57 60 | 
             
            ## Getting Started
         | 
| 58 61 |  | 
| 59 | 
            -
             | 
| 62 | 
            +
            You should configure an encryption algorithm and specify the encryption key. By default the gem uses `HS256`.
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            ```ruby
         | 
| 65 | 
            +
            JWTSessions.encryption_key = "secret"
         | 
| 66 | 
            +
            ```
         | 
| 67 | 
            +
             | 
| 68 | 
            +
            `Authorization` mixin provides helper methods which are used to retrieve access and refresh tokens from incoming requests and verify CSRF token if needed. It assumes that a token can be found either in a cookie or in a header (cookie and header names are configurable). It tries to retrieve it from headers first, then from cookies (CSRF check included) if the headers check failed.
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            ### Creating a session
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            Each token contains a payload with custom session info. The payload is a regular Ruby hash. \
         | 
| 73 | 
            +
            Usually, it contains user ID or other data which helps to identify current user but it's not necessary, the payload can be an empty hash as well.
         | 
| 74 | 
            +
             | 
| 75 | 
            +
            ```ruby
         | 
| 76 | 
            +
            > payload = { user_id: user.id }
         | 
| 77 | 
            +
            => {:user_id=>1}
         | 
| 78 | 
            +
            ```
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            Generate the session with a custom payload. By default the same payload is sewn into the session's access and refresh tokens.
         | 
| 81 | 
            +
             | 
| 82 | 
            +
            ```ruby
         | 
| 83 | 
            +
            > session = JWTSessions::Session.new(payload: payload)
         | 
| 84 | 
            +
            => #<JWTSessions::Session:0x00007fbe2cce9ea0...>
         | 
| 85 | 
            +
            ```
         | 
| 86 | 
            +
             | 
| 87 | 
            +
            Sometimes it makes sense to keep different data within the payloads of access and refresh tokens. \
         | 
| 88 | 
            +
            The access token may contain rich data including user settings, etc., while the appropriate refresh token will include only the bare minimum which will be required to reconstruct a payload for the new access token during refresh.
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            ```ruby
         | 
| 91 | 
            +
            session = JWTSessions::Session.new(payload: payload, refresh_payload: refresh_payload)
         | 
| 92 | 
            +
            ```
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            Now we can call `login` method on the session to retrieve a set of tokens.
         | 
| 95 | 
            +
             | 
| 96 | 
            +
            ```ruby
         | 
| 97 | 
            +
            > session.login
         | 
| 98 | 
            +
            => {:csrf=>"BmhxDRW5NAEIx...",
         | 
| 99 | 
            +
                :access=>"eyJhbGciOiJIUzI1NiJ9...",
         | 
| 100 | 
            +
                :access_expires_at=>"..."
         | 
| 101 | 
            +
                :refresh=>"eyJhbGciOiJIUzI1NiJ9...",
         | 
| 102 | 
            +
                :refresh_expires_at=>"..."}
         | 
| 103 | 
            +
            ```
         | 
| 104 | 
            +
             | 
| 105 | 
            +
            Access/refresh tokens automatically contain expiration time in their payload. Yet expiration times are also added to the output just in case. \
         | 
| 106 | 
            +
            The token's payload will be available in the controllers once the access (or refresh) token is authorized.
         | 
| 107 | 
            +
             | 
| 108 | 
            +
            To perform the refresh do:
         | 
| 109 | 
            +
             | 
| 110 | 
            +
            ```ruby
         | 
| 111 | 
            +
            > session.refresh(refresh_token)
         | 
| 112 | 
            +
            => {:csrf=>"+pk2SQrXHRo1iV1x4O...",
         | 
| 113 | 
            +
                :access=>"eyJhbGciOiJIUzI1...",
         | 
| 114 | 
            +
                :access_expires_at=>"..."}
         | 
| 115 | 
            +
            ```
         | 
| 116 | 
            +
             | 
| 117 | 
            +
            Available `JWTSessions::Session.new` options:
         | 
| 118 | 
            +
             | 
| 119 | 
            +
            - **payload**: a hash object with session data which will be included into an access token payload. Default is an empty hash.
         | 
| 120 | 
            +
            - **refresh_payload**: a hash object with session data which will be included into a refresh token payload. Default is a value of the access payload.
         | 
| 121 | 
            +
            - **access_claims**: a hash object with [JWT claims](https://github.com/jwt/ruby-jwt#support-for-reserved-claim-names) which will be validated within the access token payload. F.e. `{ aud: ["admin"], verify_aud: true }` meaning that the token can be used only by "admin" audience. Also, the endpoint can automatically validate claims instead. See `token_claims` method.
         | 
| 122 | 
            +
            - **refresh_claims**: a hash object with [JWT claims](https://github.com/jwt/ruby-jwt#support-for-reserved-claim-names) which will be validated within the refresh token payload.
         | 
| 123 | 
            +
            - **namespace**: a string object which helps to group sessions by a custom criteria. For example, sessions can be grouped by user ID, then it'll be possible to logout the user from all devises. More info [Sessions Namespace](#sessions-namespace).
         | 
| 124 | 
            +
            - **refresh_by_access_allowed**: a boolean value. Default is false. It links access and refresh tokens (adds refresh token ID to access payload), making it possible to perform a session refresh by the last expired access token. See [Refresh with access token](#refresh-with-access-token).
         | 
| 125 | 
            +
            - **access_exp**: an integer value. Contains an access token expiration time in seconds. The value overrides global settings. See [Expiration time](#expiration-time).
         | 
| 126 | 
            +
            - **refresh_exp**: an integer value. Contains a refresh token expiration time in seconds. The value overrides global settings. See [Expiration time](#expiration-time).
         | 
| 127 | 
            +
             | 
| 128 | 
            +
            Helper methods within `Authorization` mixin:
         | 
| 129 | 
            +
             | 
| 130 | 
            +
            - **authorize_access_request!**: validates access token within the request.
         | 
| 131 | 
            +
            - **authorize_refresh_request!**: validates refresh token within the request.
         | 
| 132 | 
            +
            - **found_token**: a raw token found within the request.
         | 
| 133 | 
            +
            - **payload**: a decoded token's payload.
         | 
| 134 | 
            +
            - **claimless_payload**: a decoded token's payload without claims validation (can be used for checking data of an expired token).
         | 
| 135 | 
            +
            - **token_claims**: the method should be defined by a developer, and is expected to return a hash-like object with claims to be validated within a token's payload.
         | 
| 60 136 |  | 
| 61 137 | 
             
            ### Rails integration
         | 
| 62 138 |  | 
| @@ -91,25 +167,6 @@ JWTSessions.private_key = OpenSSL::PKey::RSA.generate(2048) | |
| 91 167 | 
             
            JWTSessions.public_key  = JWTSessions.private_key.public_key
         | 
| 92 168 | 
             
            ```
         | 
| 93 169 |  | 
| 94 | 
            -
            Generate access/refresh/csrf tokens with a custom payload. \
         | 
| 95 | 
            -
            The payload will be available in the controllers once the access (or refresh) token is authorized. \
         | 
| 96 | 
            -
            Access/refresh tokens contain expiration time in their payload. Yet expiration times are also added to the output just in case.
         | 
| 97 | 
            -
             | 
| 98 | 
            -
            ```ruby
         | 
| 99 | 
            -
            > payload = { user_id: user.id }
         | 
| 100 | 
            -
            => {:user_id=>1}
         | 
| 101 | 
            -
             | 
| 102 | 
            -
            > session = JWTSessions::Session.new(payload: payload)
         | 
| 103 | 
            -
            => #<JWTSessions::Session:0x00007fbe2cce9ea0...>
         | 
| 104 | 
            -
             | 
| 105 | 
            -
            > session.login
         | 
| 106 | 
            -
            => {:csrf=>"BmhxDRW5NAEIx...",
         | 
| 107 | 
            -
                :access=>"eyJhbGciOiJIUzI1NiJ9...",
         | 
| 108 | 
            -
                :access_expires_at=>"..."
         | 
| 109 | 
            -
                :refresh=>"eyJhbGciOiJIUzI1NiJ9...",
         | 
| 110 | 
            -
                :refresh_expires_at=>"..."}
         | 
| 111 | 
            -
            ```
         | 
| 112 | 
            -
             | 
| 113 170 | 
             
            You can build login controller to receive access, refresh and csrf tokens in exchange for user's login/password. \
         | 
| 114 171 | 
             
            Refresh controller - to be able to get a new access token using refresh token after access is expired. \
         | 
| 115 172 | 
             
            Here is example of a simple login controller, which returns set of tokens as a plain JSON response. \
         | 
| @@ -130,12 +187,6 @@ class LoginController < ApplicationController | |
| 130 187 | 
             
            end
         | 
| 131 188 | 
             
            ```
         | 
| 132 189 |  | 
| 133 | 
            -
            Since it's not required to pass an access token when you want to perform a refresh you may need to have some data in the payload of the refresh token to allow you to construct a payload of the new access token during refresh.
         | 
| 134 | 
            -
             | 
| 135 | 
            -
            ```ruby
         | 
| 136 | 
            -
            session = JWTSessions::Session.new(payload: payload, refresh_payload: refresh_payload)
         | 
| 137 | 
            -
            ```
         | 
| 138 | 
            -
             | 
| 139 190 | 
             
            Now you can build a refresh endpoint. To protect the endpoint use before_action `authorize_refresh_request!`. \
         | 
| 140 191 | 
             
            The endpoint itself should return a renewed access token.
         | 
| 141 192 |  | 
| @@ -245,7 +296,7 @@ class SimpleApp < Sinatra::Base | |
| 245 296 | 
             
              include JWTSessions::Authorization
         | 
| 246 297 |  | 
| 247 298 | 
             
              def request_headers
         | 
| 248 | 
            -
                env.inject({}){|acc, (k,v)| acc[$1.downcase] = v if k =~ /^http_(.*)/i; acc}
         | 
| 299 | 
            +
                env.inject({}) { |acc, (k,v)| acc[$1.downcase] = v if k =~ /^http_(.*)/i; acc }
         | 
| 249 300 | 
             
              end
         | 
| 250 301 |  | 
| 251 302 | 
             
              def request_cookies
         | 
| @@ -355,6 +406,7 @@ class UsersController < ApplicationController | |
| 355 406 | 
             
              def token_claims
         | 
| 356 407 | 
             
                {
         | 
| 357 408 | 
             
                  aud: ["admin", "staff"],
         | 
| 409 | 
            +
                  verify_aud: true, # can be used locally instead of a global setting
         | 
| 358 410 | 
             
                  exp_leeway: 15 # will be used instead of default leeway only for exp claim
         | 
| 359 411 | 
             
                }
         | 
| 360 412 | 
             
              end
         | 
| @@ -384,6 +436,8 @@ JWTSessions.access_exp_time = 3600 # 1 hour in seconds | |
| 384 436 | 
             
            JWTSessions.refresh_exp_time = 604800 # 1 week in seconds
         | 
| 385 437 | 
             
            ```
         | 
| 386 438 |  | 
| 439 | 
            +
            It's defined globally, but can be overridden on a session level. See `JWTSessions::Session.new` options for more info.
         | 
| 440 | 
            +
             | 
| 387 441 | 
             
            #### CSRF and cookies
         | 
| 388 442 |  | 
| 389 443 | 
             
            In case when you use cookies as your tokens transport it gets vulnerable to CSRF. That's why both login and refresh methods of the `Session` class produce CSRF tokens for you. `Authorization` mixin expects that this token is sent with all requests except GET and HEAD in a header specified among this gem's settings (X-CSRF-Token by default). Verification will be done automatically and `Authorization` exception will be raised in case of mismatch between the token from the header and the one stored in session. \
         | 
    
        data/lib/jwt_sessions.rb
    CHANGED
    
    | @@ -136,6 +136,14 @@ module JWTSessions | |
| 136 136 | 
             
                Time.now.to_i + refresh_exp_time.to_i
         | 
| 137 137 | 
             
              end
         | 
| 138 138 |  | 
| 139 | 
            +
              def custom_access_expiration(time)
         | 
| 140 | 
            +
                Time.now.to_i + (time || access_exp_time).to_i
         | 
| 141 | 
            +
              end
         | 
| 142 | 
            +
             | 
| 143 | 
            +
              def custom_refresh_expiration(time)
         | 
| 144 | 
            +
                Time.now.to_i + (time || refresh_exp_time).to_i
         | 
| 145 | 
            +
              end
         | 
| 146 | 
            +
             | 
| 139 147 | 
             
              def header_by(token_type)
         | 
| 140 148 | 
             
                send("#{token_type}_header")
         | 
| 141 149 | 
             
              end
         | 
| @@ -29,8 +29,8 @@ module JWTSessions | |
| 29 29 | 
             
                end
         | 
| 30 30 |  | 
| 31 31 | 
             
                class << self
         | 
| 32 | 
            -
                  def create(csrf, payload, store)
         | 
| 33 | 
            -
                    new(csrf, payload, store).tap do |inst|
         | 
| 32 | 
            +
                  def create(csrf, payload, store, expiration = JWTSessions.access_expiration)
         | 
| 33 | 
            +
                    new(csrf, payload, store, SecureRandom.uuid, expiration).tap do |inst|
         | 
| 34 34 | 
             
                      store.persist_access(inst.uid, inst.csrf, inst.expiration)
         | 
| 35 35 | 
             
                    end
         | 
| 36 36 | 
             
                  end
         | 
| @@ -13,15 +13,23 @@ module JWTSessions | |
| 13 13 | 
             
                  @access_uid        = access_uid
         | 
| 14 14 | 
             
                  @access_expiration = access_expiration
         | 
| 15 15 | 
             
                  @store             = store
         | 
| 16 | 
            -
                  @uid               = options.fetch(:uid, SecureRandom.uuid | 
| 17 | 
            -
                  @expiration        = options.fetch(:expiration, JWTSessions.refresh_expiration | 
| 16 | 
            +
                  @uid               = options.fetch(:uid, nil) || SecureRandom.uuid
         | 
| 17 | 
            +
                  @expiration        = options.fetch(:expiration, nil) || JWTSessions.refresh_expiration
         | 
| 18 18 | 
             
                  @namespace         = options.fetch(:namespace, nil)
         | 
| 19 19 | 
             
                  @token             = Token.encode(options.fetch(:payload, {}).merge("uid" => uid, "exp" => expiration.to_i))
         | 
| 20 20 | 
             
                end
         | 
| 21 21 |  | 
| 22 22 | 
             
                class << self
         | 
| 23 | 
            -
                  def create(csrf, access_uid, access_expiration, store, payload, namespace)
         | 
| 24 | 
            -
                    inst = new( | 
| 23 | 
            +
                  def create(csrf, access_uid, access_expiration, store, payload, namespace, expiration = JWTSessions.refresh_expiration)
         | 
| 24 | 
            +
                    inst = new(
         | 
| 25 | 
            +
                      csrf,
         | 
| 26 | 
            +
                      access_uid,
         | 
| 27 | 
            +
                      access_expiration,
         | 
| 28 | 
            +
                      store,
         | 
| 29 | 
            +
                      payload: payload,
         | 
| 30 | 
            +
                      namespace: namespace,
         | 
| 31 | 
            +
                      expiration: expiration
         | 
| 32 | 
            +
                    )
         | 
| 25 33 | 
             
                    inst.send(:persist_in_store)
         | 
| 26 34 | 
             
                    inst
         | 
| 27 35 | 
             
                  end
         | 
    
        data/lib/jwt_sessions/session.rb
    CHANGED
    
    | @@ -20,6 +20,8 @@ module JWTSessions | |
| 20 20 | 
             
                  @refresh_claims            = options.fetch(:refresh_claims, {})
         | 
| 21 21 | 
             
                  @namespace                 = options.fetch(:namespace, nil)
         | 
| 22 22 | 
             
                  @refresh_by_access_allowed = options.fetch(:refresh_by_access_allowed, false)
         | 
| 23 | 
            +
                  @_access_exp               = options.fetch(:access_exp, nil)
         | 
| 24 | 
            +
                  @_refresh_exp              = options.fetch(:refresh_exp, nil)
         | 
| 23 25 | 
             
                end
         | 
| 24 26 |  | 
| 25 27 | 
             
                def login
         | 
| @@ -237,18 +239,26 @@ module JWTSessions | |
| 237 239 | 
             
                end
         | 
| 238 240 |  | 
| 239 241 | 
             
                def create_refresh_token
         | 
| 240 | 
            -
                  @_refresh = RefreshToken.create( | 
| 241 | 
            -
             | 
| 242 | 
            -
             | 
| 243 | 
            -
             | 
| 244 | 
            -
             | 
| 245 | 
            -
             | 
| 242 | 
            +
                  @_refresh = RefreshToken.create(
         | 
| 243 | 
            +
                    @_csrf.encoded,
         | 
| 244 | 
            +
                    @_access.uid,
         | 
| 245 | 
            +
                    @_access.expiration,
         | 
| 246 | 
            +
                    store,
         | 
| 247 | 
            +
                    refresh_payload,
         | 
| 248 | 
            +
                    namespace,
         | 
| 249 | 
            +
                    JWTSessions.custom_refresh_expiration(@_refresh_exp)
         | 
| 250 | 
            +
                  )
         | 
| 246 251 | 
             
                  @refresh_token = @_refresh.token
         | 
| 247 252 | 
             
                  link_access_to_refresh
         | 
| 248 253 | 
             
                end
         | 
| 249 254 |  | 
| 250 255 | 
             
                def create_access_token
         | 
| 251 | 
            -
                  @_access = AccessToken.create( | 
| 256 | 
            +
                  @_access = AccessToken.create(
         | 
| 257 | 
            +
                    @_csrf.encoded,
         | 
| 258 | 
            +
                    payload,
         | 
| 259 | 
            +
                    store,
         | 
| 260 | 
            +
                    JWTSessions.custom_access_expiration(@_access_exp)
         | 
| 261 | 
            +
                  )
         | 
| 252 262 | 
             
                  @access_token = @_access.token
         | 
| 253 263 | 
             
                end
         | 
| 254 264 | 
             
              end
         | 
    
        data/lib/jwt_sessions/version.rb
    CHANGED
    
    
| @@ -27,6 +27,18 @@ class TestSession < Minitest::Test | |
| 27 27 | 
             
                assert_equal payload[:test], decoded_access["test"]
         | 
| 28 28 | 
             
              end
         | 
| 29 29 |  | 
| 30 | 
            +
              def test_login_with_custom_exp
         | 
| 31 | 
            +
                @new_session = JWTSessions::Session.new(
         | 
| 32 | 
            +
                  payload: payload,
         | 
| 33 | 
            +
                  access_exp: 18000, # 5 hours in seconds
         | 
| 34 | 
            +
                  refresh_exp: 18000
         | 
| 35 | 
            +
                )
         | 
| 36 | 
            +
                assert_equal false, tokens[:refresh_expires_at] == tokens[:access_expires_at]
         | 
| 37 | 
            +
                new_tokens = @new_session.login
         | 
| 38 | 
            +
                assert_equal LOGIN_KEYS, new_tokens.keys.sort
         | 
| 39 | 
            +
                assert_equal new_tokens[:refresh_expires_at], new_tokens[:access_expires_at]
         | 
| 40 | 
            +
              end
         | 
| 41 | 
            +
             | 
| 30 42 | 
             
              def test_refresh
         | 
| 31 43 | 
             
                refreshed_tokens = session.refresh(tokens[:refresh])
         | 
| 32 44 | 
             
                decoded_access = JWTSessions::Token.decode(refreshed_tokens[:access]).first
         | 
| @@ -34,6 +46,18 @@ class TestSession < Minitest::Test | |
| 34 46 | 
             
                assert_equal payload[:test], decoded_access["test"]
         | 
| 35 47 | 
             
              end
         | 
| 36 48 |  | 
| 49 | 
            +
              def test_refresh_with_custom_exp
         | 
| 50 | 
            +
                @new_session = JWTSessions::Session.new(
         | 
| 51 | 
            +
                  payload: payload,
         | 
| 52 | 
            +
                  access_exp: 18000, # 5 hours in seconds
         | 
| 53 | 
            +
                  refresh_exp: 18000
         | 
| 54 | 
            +
                )
         | 
| 55 | 
            +
                new_tokens = @new_session.login
         | 
| 56 | 
            +
                refreshed_tokens = @new_session.refresh(new_tokens[:refresh])
         | 
| 57 | 
            +
                assert_equal LOGIN_KEYS, new_tokens.keys.sort
         | 
| 58 | 
            +
                assert_equal refreshed_tokens[:refresh_expires_at], refreshed_tokens[:access_expires_at]
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
             | 
| 37 61 | 
             
              def test_refresh_expired
         | 
| 38 62 | 
             
                JWTSessions.refresh_exp_time = 0
         | 
| 39 63 | 
             
                session = JWTSessions::Session.new(payload: payload)
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: jwt_sessions
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2. | 
| 4 | 
            +
              version: 2.4.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Yulia Oletskaya
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2019- | 
| 11 | 
            +
            date: 2019-05-01 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: jwt
         |