fresh_jwt 0.0.1 → 0.0.2
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/Gemfile +8 -1
- data/Gemfile.lock +8 -0
- data/README.md +19 -1
- data/fresh_jwt.gemspec +27 -0
- data/lib/fresh_jwt.rb +2 -0
- data/lib/fresh_jwt/entity/access_token.rb +3 -0
- data/lib/fresh_jwt/issuer.rb +13 -2
- data/lib/fresh_jwt/refresher.rb +42 -0
- data/lib/fresh_jwt/store.rb +14 -42
- data/lib/fresh_jwt/store/decorator.rb +55 -0
- data/lib/fresh_jwt/store/memory.rb +22 -0
- data/lib/fresh_jwt/store/mixin.rb +26 -0
- data/lib/fresh_jwt/store_old.rb +50 -0
- data/lib/fresh_jwt/validator.rb +1 -1
- data/lib/fresh_jwt/version.rb +1 -1
- data/main.rb +1 -1
- data/spec/fresh_jwt/issuer_spec.rb +6 -1
- data/spec/fresh_jwt/memory_spec.rb +62 -0
- data/spec/fresh_jwt/refresher_spec.rb +54 -0
- data/spec/fresh_jwt/store/store_spec.rb +23 -0
- data/spec/fresh_jwt/{store_spec.rb → store_old_spec.rb} +1 -1
- data/spec/fresh_jwt/token_spec.rb +24 -0
- data/spec/spec_helper.rb +4 -0
- metadata +131 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 603391519afdf2628ea61efed85564ece7c9bb57f9bdc17a9d526bb0fb4817c8
|
4
|
+
data.tar.gz: 50ee1e78355778ec9576dc7f51dc0ad568be71ec2d3b0322c340860a5bdbdb68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a6ef48edfaa81e251bd73fe7ad0b6bbc8a40c3fa58b6cd161833f09004f15f3629c204011df855977f75eae7994483713765421550bad1a93dca060250fa5ca
|
7
|
+
data.tar.gz: 73d729094f74fc0daa5b269897ad8f6f93c62d6794e779174ca94f43202d5bf90d46d7b7067db917a89a34017f3d5b72e73981e20f921188a50f4f683cba0f3c
|
data/Gemfile
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
# A sample Gemfile
|
3
3
|
source "https://rubygems.org"
|
4
4
|
|
5
|
+
# Specify your gem's dependencies in fresh_jwt.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
=begin
|
5
9
|
git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
|
6
10
|
|
7
11
|
ruby "2.7.3"
|
@@ -15,4 +19,7 @@ gem 'rspec'
|
|
15
19
|
# gem 'jwt_claims'
|
16
20
|
|
17
21
|
gem 'pry'
|
18
|
-
gem 'timecop'
|
22
|
+
gem 'timecop'
|
23
|
+
|
24
|
+
gem 'simplecov', require: false, group: :test
|
25
|
+
=end
|
data/Gemfile.lock
CHANGED
@@ -4,6 +4,7 @@ GEM
|
|
4
4
|
coderay (1.1.3)
|
5
5
|
concurrent-ruby (1.1.9)
|
6
6
|
diff-lcs (1.4.4)
|
7
|
+
docile (1.4.0)
|
7
8
|
dry-configurable (0.12.1)
|
8
9
|
concurrent-ruby (~> 1.0)
|
9
10
|
dry-core (~> 0.5, >= 0.5.0)
|
@@ -60,6 +61,12 @@ GEM
|
|
60
61
|
diff-lcs (>= 1.2.0, < 2.0)
|
61
62
|
rspec-support (~> 3.10.0)
|
62
63
|
rspec-support (3.10.2)
|
64
|
+
simplecov (0.21.2)
|
65
|
+
docile (~> 1.1)
|
66
|
+
simplecov-html (~> 0.11)
|
67
|
+
simplecov_json_formatter (~> 0.1)
|
68
|
+
simplecov-html (0.12.3)
|
69
|
+
simplecov_json_formatter (0.1.3)
|
63
70
|
timecop (0.9.4)
|
64
71
|
|
65
72
|
PLATFORMS
|
@@ -72,6 +79,7 @@ DEPENDENCIES
|
|
72
79
|
jwt
|
73
80
|
pry
|
74
81
|
rspec
|
82
|
+
simplecov
|
75
83
|
timecop
|
76
84
|
|
77
85
|
RUBY VERSION
|
data/README.md
CHANGED
@@ -1 +1,19 @@
|
|
1
|
-
# fresh_jwt
|
1
|
+
# fresh_jwt
|
2
|
+
|
3
|
+
Userfull links for develpoment
|
4
|
+
|
5
|
+
gist with access+refresh implementation
|
6
|
+
https://gist.github.com/jesster2k10/e626ee61d678350a21a9d3e81da2493e
|
7
|
+
|
8
|
+
api_guard
|
9
|
+
https://github.com/Gokul595/api_guard
|
10
|
+
|
11
|
+
yookassa
|
12
|
+
https://github.com/paderinandrey/yookassa
|
13
|
+
|
14
|
+
# rfc about claims
|
15
|
+
https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.7
|
16
|
+
|
17
|
+
|
18
|
+
# how to written factory bot - it is usefull for my decorator
|
19
|
+
https://twitter.com/JasonSwett/status/1408066341223821320/photo/1
|
data/fresh_jwt.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = 'fresh_jwt'
|
4
|
+
s.version = '0.0.2'
|
5
|
+
s.summary = 'Fresh JWT token!'
|
6
|
+
s.description = 'Issue access JWT & secure with refresh token'
|
7
|
+
s.authors = ["Andrei Mosin"]
|
8
|
+
s.email = 'otmosina@gmail.com'
|
9
|
+
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
10
|
+
s.test_files = s.files.grep(/^spec/)
|
11
|
+
s.homepage = 'https://rubygems.org/gems/fresh_jwt'
|
12
|
+
s.license = 'MIT'
|
13
|
+
|
14
|
+
s.platform = Gem::Platform::RUBY
|
15
|
+
s.required_ruby_version = '>= 2.7.3'
|
16
|
+
|
17
|
+
s.add_runtime_dependency 'jwt', '~> 2.2.3'
|
18
|
+
s.add_runtime_dependency 'dry-initializer', '~> 3.0'
|
19
|
+
s.add_runtime_dependency 'dry-monads', '~> 1.3'
|
20
|
+
s.add_runtime_dependency 'dry-validation', '~> 1.6'
|
21
|
+
|
22
|
+
s.add_development_dependency 'pry', '~> 0.14'
|
23
|
+
s.add_development_dependency 'rspec', '~> 3.10'
|
24
|
+
s.add_development_dependency 'simplecov', '~> 0.22'
|
25
|
+
s.add_development_dependency 'timecop', '~> 0.9'
|
26
|
+
|
27
|
+
end
|
data/lib/fresh_jwt.rb
CHANGED
@@ -13,6 +13,8 @@ require_relative 'fresh_jwt/version'
|
|
13
13
|
require_relative 'fresh_jwt/validator'
|
14
14
|
require_relative 'fresh_jwt/entity/access_token'
|
15
15
|
require_relative 'fresh_jwt/issuer'
|
16
|
+
require_relative 'fresh_jwt/store_old'
|
17
|
+
require_relative 'fresh_jwt/refresher'
|
16
18
|
|
17
19
|
|
18
20
|
# require 'dry/monads'
|
data/lib/fresh_jwt/issuer.rb
CHANGED
@@ -6,12 +6,17 @@ module FreshJwt
|
|
6
6
|
REFRESH_EXPIRATION = 60*60*24
|
7
7
|
|
8
8
|
#Payload.new(extend: val )
|
9
|
-
option :payload, ->(hash){ Payload.new(exp: FreshJwt::Expiration::ACCESS, extend: hash) }, default: -> {
|
9
|
+
option :payload, ->(hash){ Payload.new(exp: FreshJwt::Expiration::ACCESS, extend: hash) }, default: -> { Hash.new } #why we need new i do not understand, coz class is callable
|
10
10
|
# TODO: describe enum type for 2 algo
|
11
11
|
option :algorithm, default: -> { 'HS256' } #RS256
|
12
12
|
option :secret, default: -> { 'SECRET' }
|
13
13
|
option :refresh_token, proc(&:to_s), default: -> { SecureRandom.hex }
|
14
|
-
option :tokens_repo, default: -> { Store }
|
14
|
+
option :tokens_repo, ->(instance){ Store::Decorator.new(instance)}, default: -> { Store::Memory.new }
|
15
|
+
|
16
|
+
#def initialize args, &block
|
17
|
+
|
18
|
+
# super(args)
|
19
|
+
#end
|
15
20
|
|
16
21
|
def call
|
17
22
|
validate_params params
|
@@ -19,9 +24,15 @@ module FreshJwt
|
|
19
24
|
access_token = Entity::AccessToken.new(token: token)
|
20
25
|
refresh_token = Entity::RefreshToken.new(token: refresh_token)
|
21
26
|
|
27
|
+
#yield tokens_repo.transaction do
|
28
|
+
# tokens_repo.save access_token
|
29
|
+
# tokens_repo.save refresh_toke
|
30
|
+
#end
|
31
|
+
|
22
32
|
yield tokens_repo.single_transaction access_token
|
23
33
|
yield tokens_repo.single_transaction refresh_token
|
24
34
|
|
35
|
+
|
25
36
|
#result = tokens_repo.transaction do
|
26
37
|
# tokens_repo.save access_token
|
27
38
|
# tokens_repo.save refresh_token
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module FreshJwt
|
4
|
+
class Refresher
|
5
|
+
class TokenExpiredError < StandardError; end
|
6
|
+
extend Dry::Initializer
|
7
|
+
include Dry::Monads[:result, :do]
|
8
|
+
option :repo, default: -> { Store.repo }
|
9
|
+
|
10
|
+
def call(refresh_token)
|
11
|
+
token_object = yield repo.find_by_token refresh_token
|
12
|
+
yield expiration_validate token_object
|
13
|
+
access_token, refresh_token = Issuer.new.call()
|
14
|
+
|
15
|
+
|
16
|
+
Success([access_token, refresh_token])
|
17
|
+
#if token_object
|
18
|
+
# Success(token_object)
|
19
|
+
#else
|
20
|
+
# Failure(error: :token_not_found)
|
21
|
+
#end
|
22
|
+
#yield validate token
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def expiration_validate token_object
|
27
|
+
if token_object.expired?
|
28
|
+
Failure(error: TokenExpiredError.new)
|
29
|
+
else
|
30
|
+
Success()
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate token
|
35
|
+
Success()
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
#FreshJwt::Store::Manager.repo
|
41
|
+
|
42
|
+
#FreshJwt::Store::Manager
|
data/lib/fresh_jwt/store.rb
CHANGED
@@ -1,49 +1,21 @@
|
|
1
|
+
require_relative 'store/mixin'
|
2
|
+
require_relative 'store/decorator'
|
3
|
+
require_relative 'store/memory'
|
4
|
+
|
1
5
|
module FreshJwt
|
2
|
-
class Store
|
3
|
-
extend Dry::Monads[:result]
|
4
|
-
@@store = []
|
5
|
-
#{
|
6
|
-
# "token_value": {
|
7
|
-
# jti: 1,
|
8
|
-
# exp: 2,
|
9
|
-
# type: :access
|
10
|
-
# }
|
11
|
-
#}
|
12
6
|
|
13
|
-
|
7
|
+
module Store
|
8
|
+
|
9
|
+
#require 'singleton'
|
10
|
+
@@repo = nil
|
11
|
+
def self.repo= repo
|
12
|
+
# TODO add check repo needed methods
|
13
|
+
@@repo = Store::Decorator.new(repo)
|
14
14
|
|
15
|
-
@@store << token
|
16
|
-
end
|
17
|
-
def self.get token
|
18
|
-
@@store.find{ |t| t == token }
|
19
|
-
end
|
20
|
-
def self.all
|
21
|
-
@@store
|
22
|
-
end
|
23
|
-
|
24
|
-
def self.find_by_token token
|
25
|
-
@@store.find{ |t| t.token == token }
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.single_transaction token
|
29
|
-
begin
|
30
|
-
save token
|
31
|
-
return Success()
|
32
|
-
rescue Exception => error
|
33
|
-
#puts error
|
34
|
-
return Failure(error: error.message)
|
35
|
-
end
|
36
15
|
end
|
37
16
|
|
38
|
-
def self.
|
39
|
-
|
40
|
-
block.call
|
41
|
-
return Success()
|
42
|
-
rescue Exception => error
|
43
|
-
|
44
|
-
puts error
|
45
|
-
return Failure(error: error.message)
|
46
|
-
end
|
17
|
+
def self.repo
|
18
|
+
@@repo
|
47
19
|
end
|
48
20
|
end
|
49
|
-
end
|
21
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
require 'delegate'
|
3
|
+
module FreshJwt
|
4
|
+
module Store
|
5
|
+
class Decorator < SimpleDelegator
|
6
|
+
|
7
|
+
include Dry::Monads[:result]
|
8
|
+
class TokenObjectError < StandardError
|
9
|
+
DEFAULT_MESSAGE = 'Token object has wrong structure for Tokens store'
|
10
|
+
def initialize(msg = DEFAULT_MESSAGE, exception_type='token')
|
11
|
+
@exception_type = exception_type
|
12
|
+
super(msg)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def single_transaction token, type=:access
|
17
|
+
token_object = if type.to_sym == :access
|
18
|
+
Entity::AccessToken.new(token: token)
|
19
|
+
else
|
20
|
+
Entity::RefreshToken.new(token: token)
|
21
|
+
end
|
22
|
+
|
23
|
+
begin
|
24
|
+
self.save token_object
|
25
|
+
return Success()
|
26
|
+
rescue Exception => error
|
27
|
+
#puts error
|
28
|
+
return Failure(error: error.message)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# TODO this method is deprecated
|
33
|
+
def save token_object
|
34
|
+
#raise StandardError
|
35
|
+
unless token_object.respond_to?('token')
|
36
|
+
raise TokenObjectError
|
37
|
+
end
|
38
|
+
super(token_object)
|
39
|
+
end
|
40
|
+
|
41
|
+
def find_by_token token
|
42
|
+
|
43
|
+
# TODO wrap incoming method to monads
|
44
|
+
#return super(token)
|
45
|
+
#
|
46
|
+
token_object = super(token)
|
47
|
+
if token_object
|
48
|
+
Success(token_object)
|
49
|
+
else
|
50
|
+
Failure(error: :token_not_found)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module FreshJwt
|
2
|
+
module Store
|
3
|
+
class Memory
|
4
|
+
def initialize
|
5
|
+
@store = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def save token_object
|
9
|
+
@store[token_object.token] = token_object
|
10
|
+
return true
|
11
|
+
end
|
12
|
+
|
13
|
+
def find_by_token token
|
14
|
+
if token == 'unknown_token'
|
15
|
+
#require 'pry'; binding.pry
|
16
|
+
end
|
17
|
+
@store[token]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module FreshJwt
|
2
|
+
module Store
|
3
|
+
module Mixin
|
4
|
+
include Dry::Monads[:result]
|
5
|
+
def single_transaction token
|
6
|
+
begin
|
7
|
+
self.save token
|
8
|
+
return Success()
|
9
|
+
rescue Exception => error
|
10
|
+
#puts error
|
11
|
+
return Failure(error: error.message)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def find_by_token token
|
16
|
+
# TODO wrap incoming method to monads
|
17
|
+
#return super(token)
|
18
|
+
if super(token)
|
19
|
+
Success(token)
|
20
|
+
else
|
21
|
+
Failure(error: :token_not_found)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module FreshJwt
|
2
|
+
class StoreOld
|
3
|
+
extend Dry::Monads[:result]
|
4
|
+
@@store = []
|
5
|
+
#{
|
6
|
+
# "token_value": {
|
7
|
+
# jti: 1,
|
8
|
+
# exp: 2,
|
9
|
+
# type: :access
|
10
|
+
# }
|
11
|
+
#}
|
12
|
+
|
13
|
+
def self.save token
|
14
|
+
|
15
|
+
@@store << token
|
16
|
+
end
|
17
|
+
def self.get token
|
18
|
+
@@store.find{ |t| t == token }
|
19
|
+
end
|
20
|
+
def self.all
|
21
|
+
@@store
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.find_by_token token
|
25
|
+
@@store.find{ |t| t.token == token }
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.single_transaction token
|
29
|
+
begin
|
30
|
+
save token
|
31
|
+
return Success()
|
32
|
+
rescue Exception => error
|
33
|
+
#puts error
|
34
|
+
return Failure(error: error.message)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.transaction &block
|
39
|
+
begin
|
40
|
+
block.call
|
41
|
+
return Success()
|
42
|
+
rescue Exception => error
|
43
|
+
|
44
|
+
puts error
|
45
|
+
return Failure(error: error.message)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
data/lib/fresh_jwt/validator.rb
CHANGED
data/lib/fresh_jwt/version.rb
CHANGED
data/main.rb
CHANGED
@@ -29,7 +29,7 @@ p "decoded " << FreshJwt::Decoder.new().decode(token:token).to_s
|
|
29
29
|
#p JWT.decode(token, issuer.secret, 'HS256')
|
30
30
|
p "valid? " << FreshJwt::Validator.new().call(token).to_s
|
31
31
|
payload = FreshJwt::Payload.new({})
|
32
|
-
p "store: #{FreshJwt::
|
32
|
+
p "store: #{FreshJwt::StoreOld.all}"
|
33
33
|
|
34
34
|
|
35
35
|
########################################
|
@@ -12,6 +12,10 @@ RSpec.describe FreshJwt::Issuer do
|
|
12
12
|
before do
|
13
13
|
allow(SecureRandom).to receive(:hex).and_return(plain_token)
|
14
14
|
end
|
15
|
+
it 'SecureRandom always return same value' do
|
16
|
+
expect(SecureRandom).to receive(:hex).and_return(plain_token)
|
17
|
+
issuer.call
|
18
|
+
end
|
15
19
|
it 'should be kind of string' do
|
16
20
|
expect(issuer.call).to be_kind_of Array
|
17
21
|
end
|
@@ -27,7 +31,8 @@ RSpec.describe FreshJwt::Issuer do
|
|
27
31
|
#let(:store) { class_double(FreshJwt::Store)}
|
28
32
|
context 'store transaction is failed' do
|
29
33
|
before do
|
30
|
-
allow(
|
34
|
+
allow(issuer.tokens_repo).to receive(:save).and_raise(StandardError)
|
35
|
+
#allow(FreshJwt::StoreOld).to receive(:save).and_raise(StandardError)
|
31
36
|
end
|
32
37
|
it 'return empty array' do
|
33
38
|
expect(issuer.call).to be_kind_of(Failure)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
Struct.new('TokenObject', :token, :expiration)
|
2
|
+
include Dry::Monads[:result]
|
3
|
+
|
4
|
+
RSpec.describe FreshJwt::Store::Memory do
|
5
|
+
let(:memory_store) { FreshJwt::Store::Decorator.new(FreshJwt::Store::Memory.new) }
|
6
|
+
let(:wrong_token_object) { SecureRandom.hex }
|
7
|
+
let(:correct_token_object) { Struct::TokenObject.new('token') }
|
8
|
+
|
9
|
+
it 'return error when token object incorrect' do
|
10
|
+
expect{memory_store.save(wrong_token_object)}.to raise_error(FreshJwt::Store::Decorator::TokenObjectError)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'return true coz token struct is ok' do
|
14
|
+
expect(memory_store.save(correct_token_object)).to be_truthy
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '.find_by_token' do
|
18
|
+
before do
|
19
|
+
memory_store.save(correct_token_object)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'return correct token object' do
|
23
|
+
expect(memory_store.find_by_token(correct_token_object.token)).to be_success
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'return nil coz token didnt save before' do
|
27
|
+
expect(memory_store.find_by_token(SecureRandom.hex)).to be_failure
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
=begin
|
32
|
+
# TODO: remove it coz mixin do not use anymore
|
33
|
+
describe 'single_transaction mixin' do
|
34
|
+
before do
|
35
|
+
memory_store.class.send(:include, FreshJwt::Store::Mixin)
|
36
|
+
end
|
37
|
+
it 'returns Success monad' do
|
38
|
+
expect(memory_store.single_transaction(correct_token_object)).to be_kind_of(Success)
|
39
|
+
end
|
40
|
+
|
41
|
+
xit 'can find token after save via single transaction' do
|
42
|
+
#require 'pry'; binding.pry
|
43
|
+
memory_store.single_transaction(correct_token_object)
|
44
|
+
expect(memory_store.find_by_token(correct_token_object.token).value!).to eq(correct_token_object.token)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
=end
|
48
|
+
#it_behaves_like 'single_transaction mixin'
|
49
|
+
|
50
|
+
describe 'use delegator for Decorate Store' do
|
51
|
+
let(:memory_store_decorated) { FreshJwt::Store::Decorator.new(FreshJwt::Store::Memory.new) }
|
52
|
+
|
53
|
+
it 'returns Success monad' do
|
54
|
+
expect(memory_store_decorated.single_transaction(correct_token_object.token)).to be_kind_of(Success)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'can find token after save via single transaction' do
|
58
|
+
memory_store_decorated.single_transaction(correct_token_object.token)
|
59
|
+
expect(memory_store_decorated.find_by_token(correct_token_object.token).value!.token).to eq(correct_token_object.token)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# кажется причина ошибок в спеках в том, что Memory obj шарится в разных местах
|
3
|
+
# нужно видимо делать instance_double()
|
4
|
+
include Dry::Monads[:result]
|
5
|
+
RSpec.describe FreshJwt::Refresher do
|
6
|
+
let(:unknown_token) { 'unknown_token' }
|
7
|
+
let(:token) { SecureRandom.uuid }
|
8
|
+
|
9
|
+
let(:repo) { FreshJwt::Store::Memory.new }
|
10
|
+
#subject(:refresher) { }
|
11
|
+
before do
|
12
|
+
FreshJwt::Store.repo = repo
|
13
|
+
FreshJwt::Store.repo.single_transaction token
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
context 'when add token to store' do
|
18
|
+
it 'retuns success with token' do
|
19
|
+
expect(described_class.new.call(token)).to be_success
|
20
|
+
end
|
21
|
+
|
22
|
+
#TODO: add shared example for checking new issued access & refresh tokens pair
|
23
|
+
it 'retuns access & refresh tokens pair' do
|
24
|
+
expect(described_class.new.call(token).value!.size).to eq(2)
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'when invalid by differents reason' do
|
31
|
+
let(:subject) {described_class.new.call(token)}
|
32
|
+
before do
|
33
|
+
t = Time.at( Time.now.to_i + FreshJwt::Expiration::ACCESS + 1 )
|
34
|
+
Timecop.travel(t)
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'have not found a token' do
|
38
|
+
expect(described_class.new.call(unknown_token)).to be_failure
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should return failure object coz token has expire' do
|
42
|
+
expect(subject).to be_failure
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should return concrete error coz token has expire' do
|
46
|
+
expect(subject.failure).to eq(error: described_class::TokenExpiredError.new)
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.describe FreshJwt::Store do
|
4
|
+
let(:repo) { FreshJwt::Store::Memory.new }
|
5
|
+
before do
|
6
|
+
described_class.repo = repo
|
7
|
+
end
|
8
|
+
it 'return correct repo' do
|
9
|
+
expect(described_class.repo).to eq(repo)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'reponds true for repo.save call' do
|
13
|
+
expect(described_class.repo.respond_to?(:save)).to eq(true)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'reponds true for repo.find_by_token' do
|
17
|
+
expect(described_class.repo.respond_to?(:find_by_token)).to eq(true)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'reponds true for repo.single_transaction' do
|
21
|
+
expect(described_class.repo.respond_to?(:single_transaction)).to eq(true)
|
22
|
+
end
|
23
|
+
end
|
@@ -3,10 +3,34 @@
|
|
3
3
|
RSpec.describe FreshJwt::Entity::AccessToken do
|
4
4
|
let(:token) { SecureRandom.hex }
|
5
5
|
let(:access_token) { FreshJwt::Entity::AccessToken.new(token: token) }
|
6
|
+
let(:refresh_token) { FreshJwt::Entity::RefreshToken.new(token: token) }
|
7
|
+
|
6
8
|
before do
|
7
9
|
|
8
10
|
end
|
9
11
|
it 'create access token klass' do
|
10
12
|
expect(access_token.name).to eq('AccessToken')
|
11
13
|
end
|
14
|
+
|
15
|
+
it 'return false coz access not expire' do
|
16
|
+
expect(access_token.expired?).to be_falsey
|
17
|
+
end
|
18
|
+
it 'return false coz refresh not expire' do
|
19
|
+
expect(refresh_token.expired?).to be_falsey
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when future time & token has expired' do
|
23
|
+
before do
|
24
|
+
access_token
|
25
|
+
refresh_token
|
26
|
+
future_time = Time.now.to_i + FreshJwt::Expiration::REFRESH + 1
|
27
|
+
Timecop.travel(Time.at(future_time))
|
28
|
+
end
|
29
|
+
it 'return true coz access expired' do
|
30
|
+
expect(access_token.expired?).to be_truthy
|
31
|
+
end
|
32
|
+
it 'return true coz refresh expired' do
|
33
|
+
expect(refresh_token.expired?).to be_truthy
|
34
|
+
end
|
35
|
+
end
|
12
36
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -16,6 +16,10 @@
|
|
16
16
|
|
17
17
|
require_relative '../lib/fresh_jwt'
|
18
18
|
require 'timecop'
|
19
|
+
|
20
|
+
require 'simplecov'
|
21
|
+
SimpleCov.start
|
22
|
+
|
19
23
|
RSpec.configure do |config|
|
20
24
|
# rspec-expectations config goes here. You can use an alternate
|
21
25
|
# assertion/expectation library such as wrong or the stdlib/minitest
|
metadata
CHANGED
@@ -1,15 +1,127 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fresh_jwt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrei Mosin
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-06-
|
12
|
-
dependencies:
|
11
|
+
date: 2021-06-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: jwt
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.2.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.2.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: dry-initializer
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dry-monads
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: dry-validation
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.6'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.6'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.14'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.14'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.10'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.10'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: simplecov
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.22'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.22'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: timecop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.9'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.9'
|
13
125
|
description: Issue access JWT & secure with refresh token
|
14
126
|
email: otmosina@gmail.com
|
15
127
|
executables: []
|
@@ -22,6 +134,7 @@ files:
|
|
22
134
|
- Gemfile.lock
|
23
135
|
- README.md
|
24
136
|
- bin/console
|
137
|
+
- fresh_jwt.gemspec
|
25
138
|
- lib/contracts/issuer_contract.rb
|
26
139
|
- lib/fresh_jwt.rb
|
27
140
|
- lib/fresh_jwt/contract_validator.rb
|
@@ -29,18 +142,26 @@ files:
|
|
29
142
|
- lib/fresh_jwt/expiration.rb
|
30
143
|
- lib/fresh_jwt/issuer.rb
|
31
144
|
- lib/fresh_jwt/payload.rb
|
145
|
+
- lib/fresh_jwt/refresher.rb
|
32
146
|
- lib/fresh_jwt/store.rb
|
147
|
+
- lib/fresh_jwt/store/decorator.rb
|
148
|
+
- lib/fresh_jwt/store/memory.rb
|
149
|
+
- lib/fresh_jwt/store/mixin.rb
|
150
|
+
- lib/fresh_jwt/store_old.rb
|
33
151
|
- lib/fresh_jwt/validator.rb
|
34
152
|
- lib/fresh_jwt/version.rb
|
35
153
|
- main.rb
|
36
154
|
- refresher_draft.rb
|
37
155
|
- spec/fresh_jwt/issuer_spec.rb
|
38
|
-
- spec/fresh_jwt/
|
156
|
+
- spec/fresh_jwt/memory_spec.rb
|
157
|
+
- spec/fresh_jwt/refresher_spec.rb
|
158
|
+
- spec/fresh_jwt/store/store_spec.rb
|
159
|
+
- spec/fresh_jwt/store_old_spec.rb
|
39
160
|
- spec/fresh_jwt/token_spec.rb
|
40
161
|
- spec/fresh_jwt/validator_spec.rb
|
41
162
|
- spec/fresh_jwt_spec.rb
|
42
163
|
- spec/spec_helper.rb
|
43
|
-
homepage: https://rubygems.org/gems/
|
164
|
+
homepage: https://rubygems.org/gems/fresh_jwt
|
44
165
|
licenses:
|
45
166
|
- MIT
|
46
167
|
metadata: {}
|
@@ -52,7 +173,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
52
173
|
requirements:
|
53
174
|
- - ">="
|
54
175
|
- !ruby/object:Gem::Version
|
55
|
-
version:
|
176
|
+
version: 2.7.3
|
56
177
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
178
|
requirements:
|
58
179
|
- - ">="
|
@@ -65,7 +186,10 @@ specification_version: 4
|
|
65
186
|
summary: Fresh JWT token!
|
66
187
|
test_files:
|
67
188
|
- spec/fresh_jwt/issuer_spec.rb
|
68
|
-
- spec/fresh_jwt/
|
189
|
+
- spec/fresh_jwt/memory_spec.rb
|
190
|
+
- spec/fresh_jwt/refresher_spec.rb
|
191
|
+
- spec/fresh_jwt/store/store_spec.rb
|
192
|
+
- spec/fresh_jwt/store_old_spec.rb
|
69
193
|
- spec/fresh_jwt/token_spec.rb
|
70
194
|
- spec/fresh_jwt/validator_spec.rb
|
71
195
|
- spec/fresh_jwt_spec.rb
|