jwt_keeper 2.0.0 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +2 -2
- data/.travis.yml +13 -7
- data/Gemfile +2 -0
- data/README.md +20 -12
- data/Rakefile +5 -1
- data/jwt_keeper.gemspec +8 -8
- data/lib/generators/{keeper → jwt_keeper}/install/install_generator.rb +2 -2
- data/lib/generators/templates/jwt_keeper.rb +13 -2
- data/lib/jwt_keeper/configuration.rb +13 -1
- data/lib/jwt_keeper/controller.rb +58 -50
- data/lib/jwt_keeper/engine.rb +2 -3
- data/lib/jwt_keeper/token.rb +38 -14
- data/lib/jwt_keeper/version.rb +1 -1
- data/spec/lib/{keeper → jwt_keeper}/configuration_spec.rb +0 -0
- data/spec/lib/{keeper → jwt_keeper}/controller_spec.rb +39 -51
- data/spec/lib/{keeper → jwt_keeper}/datastore_spec.rb +0 -0
- data/spec/lib/{keeper → jwt_keeper}/token_spec.rb +51 -6
- data/spec/lib/jwt_keeper_spec.rb +29 -0
- data/spec/spec_helper.rb +10 -10
- metadata +45 -46
- data/spec/lib/keeper_spec.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b2c902b7848d7fe72b7ad437badc5cf549b299d4b2ae2dc9150b7f42d63855a3
|
4
|
+
data.tar.gz: 77a18a0cfeb2b198f13affa3792feaeb1235d5fa2f6ae599038bb3e2245b41b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bc177da58d088bdaeeba97049628d0908f05ef9bd717674a34b511a14e70289604a87647c01853ec3aeb988fb80cd95f4c4a96d0741a1133efc72f2138b9bd98
|
7
|
+
data.tar.gz: 97dde3b11a5584357028abb7d9523d548c53ccb8e0211a4b31089f177747c1d9fca7521bf4984837a540ec2c46080a51adacd30e322fcd4408e6d8d732cad21e
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
@@ -1,20 +1,26 @@
|
|
1
1
|
language: ruby
|
2
|
+
cache: bundler
|
2
3
|
rvm:
|
3
|
-
- 2.
|
4
|
-
- 2.
|
5
|
-
- 2.
|
6
|
-
- 2.3.0
|
4
|
+
- 2.4.5
|
5
|
+
- 2.5.3
|
6
|
+
- 2.6.1
|
7
7
|
- ruby-head
|
8
8
|
matrix:
|
9
9
|
allow_failures:
|
10
10
|
- rvm: ruby-head
|
11
|
-
addons:
|
12
|
-
code_climate:
|
13
|
-
repo_token: f69bb189f348c1d7992d8ed8690d0a2c9c885c1aac45e2f4d48732034592b37b
|
14
11
|
services:
|
15
12
|
- redis-server
|
16
13
|
env:
|
17
14
|
global:
|
18
15
|
- REDIS_URL=redis://localhost:6379
|
16
|
+
- CC_TEST_REPORTER_ID=f69bb189f348c1d7992d8ed8690d0a2c9c885c1aac45e2f4d48732034592b37b
|
17
|
+
before_script:
|
18
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
19
|
+
- chmod +x ./cc-test-reporter
|
20
|
+
- ./cc-test-reporter before-build
|
21
|
+
script:
|
22
|
+
- bundle exec rspec
|
23
|
+
after_script:
|
24
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
19
25
|
notifications:
|
20
26
|
email: false
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# JWT Keeper
|
2
|
+
[![Gem Version](https://img.shields.io/gem/v/jwt_keeper.svg?maxAge=2592000)](https://rubygems.org/gems/jwt_keeper)
|
2
3
|
[![Build Status](https://img.shields.io/travis/sirwolfgang/jwt_keeper/master.svg)](https://travis-ci.org/sirwolfgang/jwt_keeper)
|
3
4
|
[![Dependency Status](https://img.shields.io/gemnasium/sirwolfgang/jwt_keeper.svg)](https://gemnasium.com/sirwolfgang/jwt_keeper)
|
4
5
|
[![Code Climate](https://img.shields.io/codeclimate/github/sirwolfgang/jwt_keeper.svg)](https://codeclimate.com/github/sirwolfgang/jwt_keeper)
|
@@ -8,9 +9,9 @@
|
|
8
9
|
An managing interface layer for handling the creation and validation of JWTs.
|
9
10
|
|
10
11
|
## Setup
|
11
|
-
- Add `gem 'jwt_keeper'
|
12
|
+
- Add `gem 'jwt_keeper'` to Gemfile
|
12
13
|
- Run `rails generate keeper:install`
|
13
|
-
- Configure `config/initializers/
|
14
|
+
- Configure `config/initializers/jwt_keeper.rb`
|
14
15
|
- Done
|
15
16
|
|
16
17
|
## Basic Usage
|
@@ -28,14 +29,15 @@ raw_token_string = token.to_jwt
|
|
28
29
|
```
|
29
30
|
|
30
31
|
## Rails Usage
|
31
|
-
The designed rails token flow is to receive and respond to requests with the token being present in the `Authorization` part of the header. This is to allow us to seamlessly rotate the tokens on the fly without having to rebuff the request as part of the user flow. Automatic rotation happens as part of the `require_authentication` action, meaning that you will always get the latest token data as
|
32
|
-
|
33
|
-
|
32
|
+
The designed rails token flow is to receive and respond to requests with the token being present in the `Authorization` part of the header. This is to allow us to seamlessly rotate the tokens on the fly without having to rebuff the request as part of the user flow. Automatic rotation happens as part of the `require_authentication` action, meaning that you will always get the latest token data as created by `generate_claims` in your controllers. This new token is added to the response with the `write_authentication_token` action.
|
33
|
+
|
34
|
+
```bash
|
35
|
+
rake generate jwt_keeper:install
|
36
|
+
```
|
34
37
|
|
35
38
|
```ruby
|
36
39
|
class ApplicationController < ActionController::Base
|
37
40
|
before_action :require_authentication
|
38
|
-
after_action :respond_with_authentication
|
39
41
|
|
40
42
|
def not_authenticated
|
41
43
|
# Overload to return status 401
|
@@ -47,7 +49,7 @@ class ApplicationController < ActionController::Base
|
|
47
49
|
|
48
50
|
def regenerate_claims(old_token)
|
49
51
|
# Overload to update claims on automatic rotation.
|
50
|
-
current_user = User.find(
|
52
|
+
current_user = User.find(old_token.claims[:uid])
|
51
53
|
{ uid: current_user.id, usn: current_user.email }
|
52
54
|
end
|
53
55
|
end
|
@@ -56,22 +58,25 @@ end
|
|
56
58
|
```ruby
|
57
59
|
class SessionsController < ApplicationController
|
58
60
|
skip_before_action :require_authentication, only: :create
|
59
|
-
skip_after_action :respond_with_authentication, only: :destroy
|
60
61
|
|
61
62
|
# POST /sessions
|
62
63
|
def create
|
63
|
-
|
64
|
+
token = JWTKeeper::Token.create(uid: @user.id, usn: @user.email)
|
65
|
+
write_authentication_token(token)
|
64
66
|
end
|
65
67
|
|
66
68
|
# PATCH/PUT /sessions
|
67
69
|
def update
|
68
|
-
|
70
|
+
token = read_authentication_token
|
71
|
+
token.rotate
|
72
|
+
write_authentication_token(token)
|
69
73
|
end
|
70
74
|
|
71
75
|
# DELETE /sessions
|
72
76
|
def destroy
|
73
|
-
|
74
|
-
|
77
|
+
token = read_authentication_token
|
78
|
+
token.revoke
|
79
|
+
clear_authentication_token
|
75
80
|
end
|
76
81
|
```
|
77
82
|
|
@@ -81,3 +86,6 @@ Hard Invalidation is a permanent revocation of the token. The primary cases of t
|
|
81
86
|
|
82
87
|
### Soft Invalidation
|
83
88
|
Soft Invalidation is the process of triggering a rotation upon the next time a token is seen in a request. On the global scale this is done when there is a version mismatch in the config. Utilizing the rails controller flow, this method works even if you have two different versions of your app deployed and requests bounce back and forth; Making rolling deployments and rollbacks completely seamless. To rotate a single token, like in the case of a change of user permissions, simply use the class(`Token.rotate`) method to flag the token for regeneration.
|
89
|
+
|
90
|
+
## Cookie Locking
|
91
|
+
Cookie locking is the practice of securing the JWT by pairing it with a secure/httponly cookie. When a JWT is created, part of the secret used to sign it is a one time generated key that is stored in a matching cookie. The cookie and JWT thus must be sent together to be considered valid. The effective result makes it extremely hard to hijack a session by stealing the JWT. This reduces the surface area of XSS considerably.
|
data/Rakefile
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bundler/gem_tasks'
|
2
4
|
require 'rspec/core/rake_task'
|
3
5
|
|
4
|
-
RSpec::Core::RakeTask.new
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
7
|
+
t.rspec_opts = '--format documentation'
|
8
|
+
end
|
5
9
|
|
6
10
|
task default: :spec
|
7
11
|
task test: :spec
|
data/jwt_keeper.gemspec
CHANGED
@@ -9,11 +9,11 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ['David Rivera', 'Zane Wolfgang Pickett']
|
10
10
|
spec.email = ['david.r.rivera193@gmail.com', 'sirwolfgang@users.noreply.github.com']
|
11
11
|
spec.summary = 'JWT for Rails made easy'
|
12
|
-
spec.description = '
|
12
|
+
spec.description = 'A managing interface layer for handling the creation and validation of JWTs'
|
13
13
|
spec.homepage = 'https://github.com/sirwolfgang/jwt_keeper'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
|
-
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(/^example\//) }
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ['lib']
|
@@ -23,14 +23,14 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.add_development_dependency 'yard'
|
24
24
|
spec.add_development_dependency 'rubocop'
|
25
25
|
spec.add_development_dependency 'dotenv'
|
26
|
+
spec.add_development_dependency 'pry'
|
26
27
|
|
27
|
-
spec.add_development_dependency 'rspec', '~> 3.
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.8'
|
28
29
|
spec.add_development_dependency 'fuubar'
|
29
30
|
spec.add_development_dependency 'simplecov'
|
30
|
-
spec.add_development_dependency 'codeclimate-test-reporter'
|
31
31
|
|
32
|
-
spec.add_dependency 'redis'
|
33
|
-
spec.add_dependency 'rails'
|
34
|
-
spec.add_dependency 'activesupport'
|
35
|
-
spec.add_dependency 'jwt', '
|
32
|
+
spec.add_dependency 'redis'
|
33
|
+
spec.add_dependency 'rails'
|
34
|
+
spec.add_dependency 'activesupport'
|
35
|
+
spec.add_dependency 'jwt', '>= 1.5'
|
36
36
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'rails/generators/base'
|
2
2
|
|
3
|
-
module
|
3
|
+
module JwtKeeper
|
4
4
|
class InstallGenerator < Rails::Generators::Base
|
5
5
|
source_root File.expand_path('../../../templates', __FILE__)
|
6
6
|
|
@@ -9,7 +9,7 @@ module JWTKeeper
|
|
9
9
|
# @example Install
|
10
10
|
# rails generate keeper:install
|
11
11
|
def copy_files
|
12
|
-
copy_file 'jwt_keeper.rb', 'config/initializers/
|
12
|
+
copy_file 'jwt_keeper.rb', 'config/initializers/jwt_keeper.rb'
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
JWTKeeper.configure do |config|
|
2
2
|
# The time to expire for the tokens
|
3
|
-
# config.expiry =
|
3
|
+
# config.expiry = 1.hour
|
4
4
|
|
5
5
|
# The hashing method to for the tokens
|
6
6
|
# Options:
|
@@ -28,5 +28,16 @@ JWTKeeper.configure do |config|
|
|
28
28
|
# config.redis_connection = Redis.new(connection_options)
|
29
29
|
|
30
30
|
# A unique idenfitier for the token version.
|
31
|
-
# config.version
|
31
|
+
# config.version = 1
|
32
|
+
|
33
|
+
# Use a httponly/secure cookie secret to prevent session hijacking
|
34
|
+
# config.cookie_lock = true
|
35
|
+
|
36
|
+
# Used to turn off TLS only mode on the cookie, for development mode. Defaults to true
|
37
|
+
# config.cookie_secure = !(Rails.env.test? || Rails.env.development?)
|
38
|
+
|
39
|
+
# Used to limit or lock down the allowed domains for the jwt/cookie
|
40
|
+
# http://api.rubyonrails.org/classes/ActionDispatch/Cookies.html
|
41
|
+
# Defaults the value of :all
|
42
|
+
# config.cookie_domain = :all
|
32
43
|
end
|
@@ -7,7 +7,10 @@ module JWTKeeper
|
|
7
7
|
issuer: 'api.example.com',
|
8
8
|
audience: 'example.com',
|
9
9
|
redis_connection: nil,
|
10
|
-
version: nil
|
10
|
+
version: nil,
|
11
|
+
cookie_lock: false,
|
12
|
+
cookie_secure: true,
|
13
|
+
cookie_domain: :all
|
11
14
|
}.freeze
|
12
15
|
|
13
16
|
# Creates a new Configuration from the passed in parameters
|
@@ -26,5 +29,14 @@ module JWTKeeper
|
|
26
29
|
ver: JWTKeeper.configuration.version # Version
|
27
30
|
}
|
28
31
|
end
|
32
|
+
|
33
|
+
# @!visibility private
|
34
|
+
def cookie_options
|
35
|
+
{
|
36
|
+
domain: JWTKeeper.configuration.cookie_domain,
|
37
|
+
secure: JWTKeeper.configuration.cookie_secure,
|
38
|
+
httponly: true
|
39
|
+
}
|
40
|
+
end
|
29
41
|
end
|
30
42
|
end
|
@@ -1,66 +1,74 @@
|
|
1
1
|
module JWTKeeper
|
2
2
|
module Controller
|
3
|
-
|
4
|
-
klass.class_eval do
|
5
|
-
include InstanceMethods
|
6
|
-
end
|
7
|
-
end
|
3
|
+
extend ActiveSupport::Concern
|
8
4
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
return not_authenticated if token.nil?
|
5
|
+
# Available to be used as a before_action by the application's controllers. This is
|
6
|
+
# the main logical section for decoding, and automatically rotating tokens
|
7
|
+
# @return [void]
|
8
|
+
def require_authentication
|
9
|
+
token = read_authentication_token
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
self.authentication_token = token
|
20
|
-
end
|
21
|
-
|
22
|
-
authenticated(token)
|
11
|
+
if token.nil?
|
12
|
+
clear_authentication_token
|
13
|
+
return not_authenticated
|
23
14
|
end
|
24
15
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def regenerate_claims(old_token)
|
16
|
+
if token.version_mismatch? || token.pending?
|
17
|
+
new_claims = regenerate_claims(token)
|
18
|
+
token.rotate(new_claims)
|
29
19
|
end
|
30
20
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
21
|
+
write_authentication_token(token)
|
22
|
+
authenticated(token)
|
23
|
+
end
|
35
24
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
25
|
+
# Decodes and returns the token
|
26
|
+
# @return [Token] the token read from request
|
27
|
+
def read_authentication_token
|
28
|
+
return nil unless request.headers['Authorization']
|
29
|
+
@authentication_token ||=
|
30
|
+
JWTKeeper::Token.find(
|
31
|
+
request.headers['Authorization'].split.last,
|
32
|
+
cookies.signed['jwt_keeper']
|
33
|
+
)
|
34
|
+
end
|
41
35
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
36
|
+
# Encodes and writes the token
|
37
|
+
# @param token [Token] The token to be written
|
38
|
+
# @return [Token] the token written to response
|
39
|
+
def write_authentication_token(token)
|
40
|
+
return clear_authentication_token if token.nil?
|
41
|
+
response.headers['Authorization'] = "Bearer #{token.to_jwt}"
|
42
|
+
cookies.signed['jwt_keeper'] = token.to_cookie
|
43
|
+
@authentication_token = token
|
44
|
+
end
|
46
45
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
46
|
+
# delets the authentication token
|
47
|
+
# @return [void]
|
48
|
+
def clear_authentication_token
|
49
|
+
response.headers['Authorization'] = nil
|
50
|
+
cookies.delete('jwt_keeper')
|
51
|
+
@authentication_token = nil
|
52
|
+
end
|
53
53
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
54
|
+
# The default action for denying non-authenticated connections.
|
55
|
+
# You can override this method in your controllers
|
56
|
+
# @return [void]
|
57
|
+
def not_authenticated
|
58
|
+
redirect_to root_path
|
59
|
+
end
|
59
60
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
# The default action for accepting authenticated connections.
|
62
|
+
# You can override this method in your controllers
|
63
|
+
# @return [void]
|
64
|
+
def authenticated(token)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Invoked by the require_authentication method as part of the automatic rotation
|
68
|
+
# process. The application should override this method to include the necessary
|
69
|
+
# claims.
|
70
|
+
# @return [void]
|
71
|
+
def regenerate_claims(old_token)
|
64
72
|
end
|
65
73
|
end
|
66
74
|
end
|
data/lib/jwt_keeper/engine.rb
CHANGED
@@ -2,10 +2,9 @@ require 'jwt_keeper'
|
|
2
2
|
require 'rails'
|
3
3
|
|
4
4
|
module JWTKeeper
|
5
|
-
#
|
6
|
-
# With the plugin logic.
|
5
|
+
# Includes JWTKeeper into ActionController
|
7
6
|
class Engine < ::Rails::Engine
|
8
|
-
initializer 'extend Controller with
|
7
|
+
initializer 'extend Controller with jwt_keeper' do |_app|
|
9
8
|
ActionController::Base.send(:include, JWTKeeper::Controller)
|
10
9
|
end
|
11
10
|
end
|
data/lib/jwt_keeper/token.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
module JWTKeeper
|
2
|
+
# This class acts as the main interface to wrap the concerns of JWTs. Handling everything from
|
3
|
+
# encoding to invalidation.
|
2
4
|
class Token
|
3
|
-
attr_accessor :claims
|
5
|
+
attr_accessor :claims, :cookie_secret
|
4
6
|
|
5
7
|
# Initalizes a new web token
|
6
8
|
# @param private_claims [Hash] the custom claims to encode
|
7
|
-
|
9
|
+
# @param cookie_secret [String] the cookie secret to use during encoding
|
10
|
+
# @return [void]
|
11
|
+
def initialize(private_claims = {}, cookie_secret = nil)
|
12
|
+
@cookie_secret = cookie_secret
|
8
13
|
@claims = {
|
9
14
|
nbf: DateTime.now.to_i, # not before
|
10
15
|
iat: DateTime.now.to_i, # issued at
|
@@ -12,23 +17,26 @@ module JWTKeeper
|
|
12
17
|
}
|
13
18
|
@claims.merge!(JWTKeeper.configuration.base_claims)
|
14
19
|
@claims.merge!(private_claims)
|
20
|
+
@claims[:exp] = @claims[:exp].to_i if @claims[:exp].is_a?(Time)
|
15
21
|
end
|
16
22
|
|
17
23
|
# Creates a new web token
|
18
24
|
# @param private_claims [Hash] the custom claims to encode
|
19
25
|
# @return [Token] token object
|
20
26
|
def self.create(private_claims)
|
21
|
-
|
27
|
+
cookie_secret = SecureRandom.hex(16) if JWTKeeper.configuration.cookie_lock
|
28
|
+
new(private_claims, cookie_secret)
|
22
29
|
end
|
23
30
|
|
24
31
|
# Decodes and validates an existing token
|
25
32
|
# @param raw_token [String] the raw token
|
33
|
+
# @param cookie_secret [String] the cookie secret
|
26
34
|
# @return [Token] token object
|
27
|
-
def self.find(raw_token)
|
28
|
-
claims = decode(raw_token)
|
35
|
+
def self.find(raw_token, cookie_secret = nil)
|
36
|
+
claims = decode(raw_token, cookie_secret)
|
29
37
|
return nil if claims.nil?
|
30
38
|
|
31
|
-
new_token = new(claims)
|
39
|
+
new_token = new(claims, cookie_secret)
|
32
40
|
return nil if new_token.revoked?
|
33
41
|
new_token
|
34
42
|
end
|
@@ -37,12 +45,14 @@ module JWTKeeper
|
|
37
45
|
# is inherently ignored by the token's exp check and then rewritten with the revokation on
|
38
46
|
# rotate.
|
39
47
|
# @param token_jti [String] the token unique id
|
48
|
+
# @return [void]
|
40
49
|
def self.rotate(token_jti)
|
41
50
|
Datastore.rotate(token_jti, JWTKeeper.configuration.expiry.from_now.to_i)
|
42
51
|
end
|
43
52
|
|
44
53
|
# Revokes a web token
|
45
54
|
# @param token_jti [String] the token unique id
|
55
|
+
# @return [void]
|
46
56
|
def self.revoke(token_jti)
|
47
57
|
Datastore.revoke(token_jti, JWTKeeper.configuration.expiry.from_now.to_i)
|
48
58
|
end
|
@@ -55,17 +65,20 @@ module JWTKeeper
|
|
55
65
|
|
56
66
|
# Revokes and creates a new web token
|
57
67
|
# @param new_claims [Hash] Used to override and update claims during rotation
|
58
|
-
# @return [
|
68
|
+
# @return [Token]
|
59
69
|
def rotate(new_claims = nil)
|
60
70
|
revoke
|
61
71
|
|
62
72
|
new_claims ||= claims.except(:iss, :aud, :exp, :nbf, :iat, :jti)
|
63
|
-
new_token = self.class.
|
73
|
+
new_token = self.class.create(new_claims)
|
74
|
+
|
64
75
|
@claims = new_token.claims
|
76
|
+
@cookie_secret = new_token.cookie_secret
|
65
77
|
self
|
66
78
|
end
|
67
79
|
|
68
80
|
# Revokes a web token
|
81
|
+
# @return [void]
|
69
82
|
def revoke
|
70
83
|
return if invalid?
|
71
84
|
Datastore.revoke(id, claims[:exp] - DateTime.now.to_i)
|
@@ -98,19 +111,28 @@ module JWTKeeper
|
|
98
111
|
# Checks if the token invalid?
|
99
112
|
# @return [Boolean]
|
100
113
|
def invalid?
|
101
|
-
self.class.decode(encode).nil? || revoked?
|
114
|
+
self.class.decode(encode, cookie_secret).nil? || revoked?
|
102
115
|
end
|
103
116
|
|
104
117
|
# Encodes the jwt
|
105
|
-
# @return [String]
|
118
|
+
# @return [String] the encoded jwt
|
106
119
|
def to_jwt
|
107
120
|
encode
|
108
121
|
end
|
109
122
|
alias to_s to_jwt
|
110
123
|
|
124
|
+
# Encodes the cookie
|
125
|
+
# @return [Hash] the cookie options
|
126
|
+
def to_cookie
|
127
|
+
{
|
128
|
+
value: cookie_secret,
|
129
|
+
expires: Time.at(claims[:exp])
|
130
|
+
}.merge(JWTKeeper.configuration.cookie_options)
|
131
|
+
end
|
132
|
+
|
111
133
|
# @!visibility private
|
112
|
-
def self.decode(raw_token)
|
113
|
-
JWT.decode(raw_token, JWTKeeper.configuration.secret, true,
|
134
|
+
def self.decode(raw_token, cookie_secret)
|
135
|
+
JWT.decode(raw_token, JWTKeeper.configuration.secret.to_s + cookie_secret.to_s, true,
|
114
136
|
algorithm: JWTKeeper.configuration.algorithm,
|
115
137
|
verify_iss: true,
|
116
138
|
verify_aud: true,
|
@@ -118,7 +140,6 @@ module JWTKeeper
|
|
118
140
|
verify_sub: false,
|
119
141
|
verify_jti: false,
|
120
142
|
leeway: 0,
|
121
|
-
|
122
143
|
iss: JWTKeeper.configuration.issuer,
|
123
144
|
aud: JWTKeeper.configuration.audience
|
124
145
|
).first.symbolize_keys
|
@@ -131,7 +152,10 @@ module JWTKeeper
|
|
131
152
|
|
132
153
|
# @!visibility private
|
133
154
|
def encode
|
134
|
-
JWT.encode(claims,
|
155
|
+
JWT.encode(claims,
|
156
|
+
JWTKeeper.configuration.secret.to_s + cookie_secret.to_s,
|
157
|
+
JWTKeeper.configuration.algorithm
|
158
|
+
)
|
135
159
|
end
|
136
160
|
end
|
137
161
|
end
|
data/lib/jwt_keeper/version.rb
CHANGED
File without changes
|
@@ -4,10 +4,22 @@ RSpec.describe JWTKeeper do
|
|
4
4
|
describe 'Controller' do
|
5
5
|
include_context 'initialize config'
|
6
6
|
|
7
|
-
let(:token) { JWTKeeper::Token.create(claim: "
|
7
|
+
let(:token) { JWTKeeper::Token.create(claim: "The Earth is Flat") }
|
8
8
|
subject(:test_controller) do
|
9
|
+
cookies_klass = Class.new(Hash) do
|
10
|
+
def signed
|
11
|
+
self
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
message_klass = Class.new(Hash) do
|
16
|
+
def headers
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
9
21
|
instance = Class.new do
|
10
|
-
attr_accessor :request, :response
|
22
|
+
attr_accessor :request, :response, :cookies
|
11
23
|
include RSpec::Mocks::ExampleMethods
|
12
24
|
include JWTKeeper::Controller
|
13
25
|
|
@@ -27,20 +39,21 @@ RSpec.describe JWTKeeper do
|
|
27
39
|
end
|
28
40
|
end.new
|
29
41
|
|
30
|
-
instance.request
|
31
|
-
|
32
|
-
instance.
|
33
|
-
|
42
|
+
instance.request = message_klass.new
|
43
|
+
instance.response = message_klass.new
|
44
|
+
instance.cookies = cookies_klass.new
|
45
|
+
instance.request['Authorization'] = "Bearer #{token}"
|
34
46
|
instance
|
35
47
|
end
|
36
48
|
|
37
49
|
describe '#included' do
|
38
50
|
it { is_expected.to respond_to(:require_authentication) }
|
39
|
-
it { is_expected.to respond_to(:
|
40
|
-
it { is_expected.to respond_to(:
|
41
|
-
it { is_expected.to respond_to(:
|
51
|
+
it { is_expected.to respond_to(:read_authentication_token) }
|
52
|
+
it { is_expected.to respond_to(:write_authentication_token) }
|
53
|
+
it { is_expected.to respond_to(:clear_authentication_token) }
|
42
54
|
it { is_expected.to respond_to(:not_authenticated) }
|
43
55
|
it { is_expected.to respond_to(:authenticated) }
|
56
|
+
it { is_expected.to respond_to(:regenerate_claims) }
|
44
57
|
end
|
45
58
|
|
46
59
|
describe '#require_authentication' do
|
@@ -56,7 +69,7 @@ RSpec.describe JWTKeeper do
|
|
56
69
|
|
57
70
|
it 'does not rotates the token' do
|
58
71
|
expect { subject.require_authentication }.to_not change {
|
59
|
-
subject.
|
72
|
+
subject.read_authentication_token.id
|
60
73
|
}
|
61
74
|
end
|
62
75
|
end
|
@@ -90,7 +103,7 @@ RSpec.describe JWTKeeper do
|
|
90
103
|
|
91
104
|
it 'rotates the token' do
|
92
105
|
expect { subject.require_authentication }.to change {
|
93
|
-
subject.
|
106
|
+
subject.read_authentication_token.id
|
94
107
|
}
|
95
108
|
end
|
96
109
|
end
|
@@ -108,7 +121,7 @@ RSpec.describe JWTKeeper do
|
|
108
121
|
|
109
122
|
it 'rotates the token' do
|
110
123
|
expect { subject.require_authentication }.to change {
|
111
|
-
subject.
|
124
|
+
subject.read_authentication_token.id
|
112
125
|
}
|
113
126
|
end
|
114
127
|
end
|
@@ -125,52 +138,27 @@ RSpec.describe JWTKeeper do
|
|
125
138
|
end
|
126
139
|
|
127
140
|
it 'is used to update the token claims on rotation' do
|
128
|
-
expect(subject.
|
129
|
-
|
130
|
-
expect(subject.
|
141
|
+
expect(subject.read_authentication_token.claims[:regenerate_claims]).to be nil
|
142
|
+
subject.require_authentication
|
143
|
+
expect(subject.read_authentication_token.claims[:regenerate_claims]).to be true
|
131
144
|
end
|
132
145
|
end
|
133
146
|
|
134
|
-
describe '#
|
135
|
-
before do
|
136
|
-
subject.
|
147
|
+
describe '#clear_authentication_token' do
|
148
|
+
before :each do
|
149
|
+
subject.write_authentication_token(JWTKeeper::Token.create({}))
|
137
150
|
end
|
138
151
|
|
139
|
-
it '
|
140
|
-
subject.
|
141
|
-
|
152
|
+
it 'clears the cookie' do
|
153
|
+
expect(subject.cookies.signed['jwt_keeper']).not_to be_nil
|
154
|
+
subject.clear_authentication_token
|
155
|
+
expect(subject.cookies.signed['jwt_keeper']).to be_nil
|
142
156
|
end
|
143
|
-
end
|
144
157
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
end
|
150
|
-
end
|
151
|
-
context 'no token in request' do
|
152
|
-
before do
|
153
|
-
token = JWTKeeper::Token.create(exp: 3.hours.ago)
|
154
|
-
subject.request =
|
155
|
-
instance_double('Request', headers: { 'Authorization' => "Bearer #{token}" })
|
156
|
-
end
|
157
|
-
|
158
|
-
it 'returns nil' do
|
159
|
-
expect(subject.authentication_token).to be nil
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
describe '#redirect_back_or_to' do
|
165
|
-
let(:path) { 'http://www.example.com' }
|
166
|
-
|
167
|
-
before do
|
168
|
-
allow(test_controller).to receive(:redirect_to)
|
169
|
-
end
|
170
|
-
|
171
|
-
it 'it calls redirect_to' do
|
172
|
-
subject.redirect_back_or_to(path)
|
173
|
-
expect(subject).to have_received(:redirect_to).with(path, anything)
|
158
|
+
it 'clears the header' do
|
159
|
+
expect(subject.response.headers['Authorization']).not_to be_nil
|
160
|
+
subject.clear_authentication_token
|
161
|
+
expect(subject.response.headers['Authorization']).to be_nil
|
174
162
|
end
|
175
163
|
end
|
176
164
|
|
File without changes
|
@@ -3,14 +3,27 @@ require 'spec_helper'
|
|
3
3
|
module JWTKeeper
|
4
4
|
RSpec.describe Token do
|
5
5
|
include_context 'initialize config'
|
6
|
-
let(:private_claims) { { claim: "
|
7
|
-
let(:
|
6
|
+
let(:private_claims) { { claim: "The Earth is Flat" } }
|
7
|
+
let(:token) { described_class.create(private_claims) }
|
8
|
+
let(:raw_token) { token.to_jwt }
|
8
9
|
|
9
10
|
describe '.create' do
|
10
11
|
subject { described_class.create(private_claims) }
|
11
12
|
|
12
13
|
it { is_expected.to be_instance_of described_class }
|
13
14
|
it { expect(subject.claims[:claim]).to eql private_claims[:claim] }
|
15
|
+
|
16
|
+
context 'with cookie_lock enabled' do
|
17
|
+
before { JWTKeeper.configure(JWTKeeper::Configuration.new(config.merge(cookie_lock: true))) }
|
18
|
+
it { expect(subject.cookie_secret).not_to be_empty }
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'when overiding default claims' do
|
22
|
+
let(:private_claims) { { exp: 1.minute.from_now.to_i } }
|
23
|
+
|
24
|
+
it { is_expected.to be_instance_of described_class }
|
25
|
+
it { expect(subject.claims[:exp]).to eql private_claims[:exp] }
|
26
|
+
end
|
14
27
|
end
|
15
28
|
|
16
29
|
describe '.find' do
|
@@ -21,15 +34,32 @@ module JWTKeeper
|
|
21
34
|
|
22
35
|
context 'with invalid token' do
|
23
36
|
let(:private_claims) { { exp: 1.hour.ago } }
|
24
|
-
|
25
37
|
it { is_expected.to be nil }
|
26
38
|
end
|
27
39
|
|
28
40
|
context 'with revoked token' do
|
29
41
|
before { described_class.find(raw_token).revoke }
|
30
|
-
|
31
42
|
it { is_expected.to be nil }
|
32
43
|
end
|
44
|
+
|
45
|
+
context 'describe with cookie locking' do
|
46
|
+
before { JWTKeeper.configure(JWTKeeper::Configuration.new(config.merge(cookie_lock: true))) }
|
47
|
+
|
48
|
+
context 'with no cookie' do
|
49
|
+
subject { described_class.find(raw_token, nil) }
|
50
|
+
it { is_expected.to be nil }
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'with bad cookie' do
|
54
|
+
subject { described_class.find(raw_token, 'BAD_COOKIE') }
|
55
|
+
it { is_expected.to be nil }
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with valid cookie' do
|
59
|
+
subject { described_class.find(raw_token, token.cookie_secret) }
|
60
|
+
it { is_expected.to be_instance_of described_class }
|
61
|
+
end
|
62
|
+
end
|
33
63
|
end
|
34
64
|
|
35
65
|
describe '.rotate' do
|
@@ -142,6 +172,7 @@ module JWTKeeper
|
|
142
172
|
end
|
143
173
|
|
144
174
|
describe '#rotate' do
|
175
|
+
before { JWTKeeper.configure(JWTKeeper::Configuration.new(config.merge(cookie_lock: true))) }
|
145
176
|
let(:old_token) { described_class.create(private_claims) }
|
146
177
|
let(:new_token) { old_token.dup.rotate }
|
147
178
|
before { new_token }
|
@@ -149,13 +180,14 @@ module JWTKeeper
|
|
149
180
|
it { expect(old_token).to be_invalid }
|
150
181
|
it { expect(new_token).to be_valid }
|
151
182
|
it { expect(old_token.claims[:claim]).to eq new_token.claims[:claim] }
|
183
|
+
it { expect(old_token.cookie_secret).not_to eq new_token.cookie_secret }
|
152
184
|
end
|
153
185
|
|
154
186
|
describe '#valid?' do
|
155
187
|
subject { described_class.create(private_claims) }
|
156
188
|
|
157
189
|
context 'when invalid' do
|
158
|
-
before { JWTKeeper.configure(JWTKeeper::Configuration.new(
|
190
|
+
before { JWTKeeper.configure(JWTKeeper::Configuration.new(config.merge(expiry: -1.hours))) }
|
159
191
|
it { is_expected.not_to be_valid }
|
160
192
|
end
|
161
193
|
|
@@ -168,13 +200,26 @@ module JWTKeeper
|
|
168
200
|
subject { described_class.create(private_claims) }
|
169
201
|
|
170
202
|
context 'when invalid' do
|
171
|
-
before { JWTKeeper.configure(JWTKeeper::Configuration.new(
|
203
|
+
before { JWTKeeper.configure(JWTKeeper::Configuration.new(config.merge(expiry: -1.hours))) }
|
172
204
|
it { is_expected.to be_invalid }
|
173
205
|
end
|
174
206
|
|
175
207
|
context 'when valid' do
|
176
208
|
it { is_expected.not_to be_invalid }
|
177
209
|
end
|
210
|
+
|
211
|
+
context 'with cookie_lock enabled' do
|
212
|
+
before { JWTKeeper.configure(JWTKeeper::Configuration.new(config.merge(cookie_lock: true))) }
|
213
|
+
|
214
|
+
context 'when invalid' do
|
215
|
+
before { JWTKeeper.configure(JWTKeeper::Configuration.new(config.merge(expiry: -1.hours))) }
|
216
|
+
it { is_expected.to be_invalid }
|
217
|
+
end
|
218
|
+
|
219
|
+
context 'when valid' do
|
220
|
+
it { is_expected.not_to be_invalid }
|
221
|
+
end
|
222
|
+
end
|
178
223
|
end
|
179
224
|
end
|
180
225
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe JWTKeeper do
|
4
|
+
describe '#configure' do
|
5
|
+
let(:new_config) { { secret: '#configure-secret' } }
|
6
|
+
|
7
|
+
context 'without block' do
|
8
|
+
before do
|
9
|
+
described_class.configure(JWTKeeper::Configuration.new(new_config))
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'sets the configuration based on param' do
|
13
|
+
expect(described_class.configuration.secret).to eql new_config[:secret]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'with block' do
|
18
|
+
before do
|
19
|
+
described_class.configure do |config|
|
20
|
+
config.secret = new_config[:secret]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'sets configuration based on the block' do
|
25
|
+
expect(described_class.configuration.secret).to eql new_config[:secret]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,15 +1,13 @@
|
|
1
|
+
require 'pry'
|
1
2
|
require 'dotenv'
|
2
3
|
Dotenv.load
|
3
4
|
|
4
5
|
require 'simplecov'
|
5
|
-
require 'codeclimate-test-reporter'
|
6
6
|
|
7
|
-
SimpleCov.formatter =
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
])
|
12
|
-
SimpleCov.start
|
7
|
+
SimpleCov.formatter = SimpleCov::Formatter::HTMLFormatter
|
8
|
+
SimpleCov.start do
|
9
|
+
add_filter '/spec/'
|
10
|
+
end
|
13
11
|
|
14
12
|
require 'rails'
|
15
13
|
require 'jwt_keeper'
|
@@ -41,18 +39,20 @@ RSpec.configure do |config|
|
|
41
39
|
end
|
42
40
|
|
43
41
|
RSpec.shared_context 'initialize config' do
|
44
|
-
let(:
|
42
|
+
let(:config) do
|
45
43
|
{
|
46
44
|
algorithm: 'HS256',
|
47
45
|
secret: 'secret',
|
48
46
|
expiry: 24.hours,
|
49
47
|
issuer: 'api.example.com',
|
50
48
|
audience: 'example.com',
|
51
|
-
redis_connection: Redis.new(url: ENV['REDIS_URL'])
|
49
|
+
redis_connection: Redis.new(url: ENV['REDIS_URL']),
|
50
|
+
version: nil,
|
51
|
+
cookie_lock: false
|
52
52
|
}
|
53
53
|
end
|
54
54
|
|
55
55
|
before(:each) do
|
56
|
-
JWTKeeper.configure(JWTKeeper::Configuration.new(
|
56
|
+
JWTKeeper.configure(JWTKeeper::Configuration.new(config))
|
57
57
|
end
|
58
58
|
end
|
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jwt_keeper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Rivera
|
8
8
|
- Zane Wolfgang Pickett
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2020-12-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -82,35 +82,35 @@ dependencies:
|
|
82
82
|
- !ruby/object:Gem::Version
|
83
83
|
version: '0'
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
|
-
name:
|
85
|
+
name: pry
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
87
87
|
requirements:
|
88
|
-
- - "
|
88
|
+
- - ">="
|
89
89
|
- !ruby/object:Gem::Version
|
90
|
-
version: '
|
90
|
+
version: '0'
|
91
91
|
type: :development
|
92
92
|
prerelease: false
|
93
93
|
version_requirements: !ruby/object:Gem::Requirement
|
94
94
|
requirements:
|
95
|
-
- - "
|
95
|
+
- - ">="
|
96
96
|
- !ruby/object:Gem::Version
|
97
|
-
version: '
|
97
|
+
version: '0'
|
98
98
|
- !ruby/object:Gem::Dependency
|
99
|
-
name:
|
99
|
+
name: rspec
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
|
-
- - "
|
102
|
+
- - "~>"
|
103
103
|
- !ruby/object:Gem::Version
|
104
|
-
version: '
|
104
|
+
version: '3.8'
|
105
105
|
type: :development
|
106
106
|
prerelease: false
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
|
-
- - "
|
109
|
+
- - "~>"
|
110
110
|
- !ruby/object:Gem::Version
|
111
|
-
version: '
|
111
|
+
version: '3.8'
|
112
112
|
- !ruby/object:Gem::Dependency
|
113
|
-
name:
|
113
|
+
name: fuubar
|
114
114
|
requirement: !ruby/object:Gem::Requirement
|
115
115
|
requirements:
|
116
116
|
- - ">="
|
@@ -124,7 +124,7 @@ dependencies:
|
|
124
124
|
- !ruby/object:Gem::Version
|
125
125
|
version: '0'
|
126
126
|
- !ruby/object:Gem::Dependency
|
127
|
-
name:
|
127
|
+
name: simplecov
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|
129
129
|
requirements:
|
130
130
|
- - ">="
|
@@ -141,59 +141,60 @@ dependencies:
|
|
141
141
|
name: redis
|
142
142
|
requirement: !ruby/object:Gem::Requirement
|
143
143
|
requirements:
|
144
|
-
- - "
|
144
|
+
- - ">="
|
145
145
|
- !ruby/object:Gem::Version
|
146
|
-
version: '
|
146
|
+
version: '0'
|
147
147
|
type: :runtime
|
148
148
|
prerelease: false
|
149
149
|
version_requirements: !ruby/object:Gem::Requirement
|
150
150
|
requirements:
|
151
|
-
- - "
|
151
|
+
- - ">="
|
152
152
|
- !ruby/object:Gem::Version
|
153
|
-
version: '
|
153
|
+
version: '0'
|
154
154
|
- !ruby/object:Gem::Dependency
|
155
155
|
name: rails
|
156
156
|
requirement: !ruby/object:Gem::Requirement
|
157
157
|
requirements:
|
158
|
-
- - "
|
158
|
+
- - ">="
|
159
159
|
- !ruby/object:Gem::Version
|
160
|
-
version: '
|
160
|
+
version: '0'
|
161
161
|
type: :runtime
|
162
162
|
prerelease: false
|
163
163
|
version_requirements: !ruby/object:Gem::Requirement
|
164
164
|
requirements:
|
165
|
-
- - "
|
165
|
+
- - ">="
|
166
166
|
- !ruby/object:Gem::Version
|
167
|
-
version: '
|
167
|
+
version: '0'
|
168
168
|
- !ruby/object:Gem::Dependency
|
169
169
|
name: activesupport
|
170
170
|
requirement: !ruby/object:Gem::Requirement
|
171
171
|
requirements:
|
172
|
-
- - "
|
172
|
+
- - ">="
|
173
173
|
- !ruby/object:Gem::Version
|
174
|
-
version: '
|
174
|
+
version: '0'
|
175
175
|
type: :runtime
|
176
176
|
prerelease: false
|
177
177
|
version_requirements: !ruby/object:Gem::Requirement
|
178
178
|
requirements:
|
179
|
-
- - "
|
179
|
+
- - ">="
|
180
180
|
- !ruby/object:Gem::Version
|
181
|
-
version: '
|
181
|
+
version: '0'
|
182
182
|
- !ruby/object:Gem::Dependency
|
183
183
|
name: jwt
|
184
184
|
requirement: !ruby/object:Gem::Requirement
|
185
185
|
requirements:
|
186
|
-
- - "
|
186
|
+
- - ">="
|
187
187
|
- !ruby/object:Gem::Version
|
188
188
|
version: '1.5'
|
189
189
|
type: :runtime
|
190
190
|
prerelease: false
|
191
191
|
version_requirements: !ruby/object:Gem::Requirement
|
192
192
|
requirements:
|
193
|
-
- - "
|
193
|
+
- - ">="
|
194
194
|
- !ruby/object:Gem::Version
|
195
195
|
version: '1.5'
|
196
|
-
description:
|
196
|
+
description: A managing interface layer for handling the creation and validation of
|
197
|
+
JWTs
|
197
198
|
email:
|
198
199
|
- david.r.rivera193@gmail.com
|
199
200
|
- sirwolfgang@users.noreply.github.com
|
@@ -213,7 +214,7 @@ files:
|
|
213
214
|
- docker-compose.yml
|
214
215
|
- example.env
|
215
216
|
- jwt_keeper.gemspec
|
216
|
-
- lib/generators/
|
217
|
+
- lib/generators/jwt_keeper/install/install_generator.rb
|
217
218
|
- lib/generators/templates/jwt_keeper.rb
|
218
219
|
- lib/jwt_keeper.rb
|
219
220
|
- lib/jwt_keeper/configuration.rb
|
@@ -223,17 +224,17 @@ files:
|
|
223
224
|
- lib/jwt_keeper/exceptions.rb
|
224
225
|
- lib/jwt_keeper/token.rb
|
225
226
|
- lib/jwt_keeper/version.rb
|
226
|
-
- spec/lib/
|
227
|
-
- spec/lib/
|
228
|
-
- spec/lib/
|
229
|
-
- spec/lib/
|
230
|
-
- spec/lib/
|
227
|
+
- spec/lib/jwt_keeper/configuration_spec.rb
|
228
|
+
- spec/lib/jwt_keeper/controller_spec.rb
|
229
|
+
- spec/lib/jwt_keeper/datastore_spec.rb
|
230
|
+
- spec/lib/jwt_keeper/token_spec.rb
|
231
|
+
- spec/lib/jwt_keeper_spec.rb
|
231
232
|
- spec/spec_helper.rb
|
232
233
|
homepage: https://github.com/sirwolfgang/jwt_keeper
|
233
234
|
licenses:
|
234
235
|
- MIT
|
235
236
|
metadata: {}
|
236
|
-
post_install_message:
|
237
|
+
post_install_message:
|
237
238
|
rdoc_options: []
|
238
239
|
require_paths:
|
239
240
|
- lib
|
@@ -248,16 +249,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
248
249
|
- !ruby/object:Gem::Version
|
249
250
|
version: '0'
|
250
251
|
requirements: []
|
251
|
-
|
252
|
-
|
253
|
-
signing_key:
|
252
|
+
rubygems_version: 3.1.4
|
253
|
+
signing_key:
|
254
254
|
specification_version: 4
|
255
255
|
summary: JWT for Rails made easy
|
256
256
|
test_files:
|
257
|
-
- spec/lib/
|
258
|
-
- spec/lib/
|
259
|
-
- spec/lib/
|
260
|
-
- spec/lib/
|
261
|
-
- spec/lib/
|
257
|
+
- spec/lib/jwt_keeper/configuration_spec.rb
|
258
|
+
- spec/lib/jwt_keeper/controller_spec.rb
|
259
|
+
- spec/lib/jwt_keeper/datastore_spec.rb
|
260
|
+
- spec/lib/jwt_keeper/token_spec.rb
|
261
|
+
- spec/lib/jwt_keeper_spec.rb
|
262
262
|
- spec/spec_helper.rb
|
263
|
-
has_rdoc:
|
data/spec/lib/keeper_spec.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe JWTKeeper do
|
4
|
-
describe '#configure' do
|
5
|
-
let(:test_config) do
|
6
|
-
{
|
7
|
-
algorithm: 'HS256',
|
8
|
-
secret: 'secret',
|
9
|
-
expiry: 24.hours,
|
10
|
-
issuer: 'api.example.com',
|
11
|
-
audience: 'example.com',
|
12
|
-
redis_connection: Redis.new(url: ENV['REDIS_URL'])
|
13
|
-
}
|
14
|
-
end
|
15
|
-
|
16
|
-
context 'without block' do
|
17
|
-
before do
|
18
|
-
described_class.configure(JWTKeeper::Configuration.new(test_config))
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'sets the configuration based on param' do
|
22
|
-
expect(described_class.configuration.secret).to eql test_config[:secret]
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
context 'with block' do
|
27
|
-
before do
|
28
|
-
described_class.configure do |config|
|
29
|
-
config.secret = test_config[:secret]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'sets configuration based on the block' do
|
34
|
-
expect(described_class.configuration.secret).to eql test_config[:secret]
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|