solidus_jwt 1.0.0 → 1.1.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/.circleci/config.yml +35 -0
- data/.gem_release.yml +5 -0
- data/.github/CODEOWNERS +4 -0
- data/.github/stale.yml +17 -0
- data/.gitignore +19 -0
- data/.rspec +1 -0
- data/.rubocop.yml +33 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +31 -0
- data/Rakefile +4 -28
- data/_config.yml +1 -0
- data/app/controllers/spree/api/oauths_controller.rb +12 -5
- data/app/decorators/solidus_jwt/spree/api/base_controller_decorator.rb +39 -0
- data/app/decorators/solidus_jwt/spree/user_decorator.rb +36 -0
- data/app/models/solidus_jwt/base_record.rb +11 -0
- data/app/models/solidus_jwt/token.rb +17 -4
- data/bin/console +17 -0
- data/bin/rails +18 -0
- data/bin/sandbox +81 -0
- data/bin/setup +8 -0
- data/config/locales/en.yml +2 -1
- data/db/migrate/20191212083655_add_foreign_key_to_users_table.rb +5 -0
- data/lib/generators/solidus_jwt/install/install_generator.rb +1 -11
- data/lib/solidus_jwt.rb +2 -0
- data/lib/solidus_jwt/concerns/decodeable.rb +5 -1
- data/lib/solidus_jwt/concerns/encodeable.rb +12 -2
- data/lib/solidus_jwt/devise_strategies/base.rb +23 -0
- data/lib/solidus_jwt/devise_strategies/password.rb +5 -11
- data/lib/solidus_jwt/devise_strategies/refresh_token.rb +7 -10
- data/lib/solidus_jwt/distributor/devise.rb +1 -1
- data/lib/solidus_jwt/engine.rb +6 -12
- data/lib/solidus_jwt/version.rb +1 -9
- data/solidus_jwt.gemspec +36 -0
- data/spec/lib/solidus_jwt/concerns/decodeable_spec.rb +0 -0
- data/spec/lib/solidus_jwt/concerns/encodeable_spec.rb +0 -0
- data/spec/lib/solidus_jwt/config_spec.rb +5 -0
- data/spec/lib/solidus_jwt/devise_strategies/password_spec.rb +76 -0
- data/spec/lib/solidus_jwt/devise_strategies/refresh_token_spec.rb +72 -0
- data/spec/lib/solidus_jwt/preferences_spec.rb +37 -0
- data/spec/lib/solidus_jwt_spec.rb +6 -0
- data/spec/models/solidus_jwt/token_spec.rb +41 -0
- data/spec/requests/spree/api/json_web_tokens_spec.rb +75 -0
- data/spec/requests/spree/api/oauths_spec.rb +120 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/shared_examples/decodeable_examples.rb +21 -0
- data/spec/support/shared_examples/encodeable_examples.rb +27 -0
- metadata +65 -227
- data/app/assets/javascripts/spree/backend/solidus_jwt.js +0 -2
- data/app/assets/javascripts/spree/frontend/solidus_jwt.js +0 -2
- data/app/assets/stylesheets/spree/backend/solidus_jwt.css +0 -4
- data/app/assets/stylesheets/spree/frontend/solidus_jwt.css +0 -4
- data/app/controllers/spree/api/base_controller/json_web_tokens.rb +0 -22
- data/app/controllers/spree/api/base_controller_decorator.rb +0 -17
- data/app/models/application_record.rb +0 -3
- data/app/models/solidus_jwt/application_record.rb +0 -9
- data/app/models/spree/user_decorator.rb +0 -34
data/bin/setup
ADDED
data/config/locales/en.yml
CHANGED
@@ -3,16 +3,6 @@ module SolidusJwt
|
|
3
3
|
class InstallGenerator < Rails::Generators::Base
|
4
4
|
class_option :auto_run_migrations, type: :boolean, default: false
|
5
5
|
|
6
|
-
def add_javascripts
|
7
|
-
append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/solidus_jwt\n"
|
8
|
-
append_file 'vendor/assets/javascripts/spree/backend/all.js', "//= require spree/backend/solidus_jwt\n"
|
9
|
-
end
|
10
|
-
|
11
|
-
def add_stylesheets
|
12
|
-
inject_into_file 'vendor/assets/stylesheets/spree/frontend/all.css', " *= require spree/frontend/solidus_jwt\n", before: /\*\//, verbose: true
|
13
|
-
inject_into_file 'vendor/assets/stylesheets/spree/backend/all.css', " *= require spree/backend/solidus_jwt\n", before: /\*\//, verbose: true
|
14
|
-
end
|
15
|
-
|
16
6
|
def add_migrations
|
17
7
|
run 'bundle exec rake railties:install:migrations FROM=solidus_jwt'
|
18
8
|
end
|
@@ -22,7 +12,7 @@ module SolidusJwt
|
|
22
12
|
if run_migrations
|
23
13
|
run 'bundle exec rake db:migrate'
|
24
14
|
else
|
25
|
-
puts 'Skipping rake db:migrate, don\'t forget to run it!'
|
15
|
+
puts 'Skipping rake db:migrate, don\'t forget to run it!' # rubocop:disable Rails/Output
|
26
16
|
end
|
27
17
|
end
|
28
18
|
end
|
data/lib/solidus_jwt.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
require 'jwt'
|
2
2
|
|
3
3
|
require 'solidus_core'
|
4
|
+
require 'solidus_support'
|
4
5
|
require 'solidus_auth_devise'
|
5
6
|
require 'solidus_jwt/engine'
|
6
7
|
|
8
|
+
require 'solidus_jwt/devise_strategies/base'
|
7
9
|
require 'solidus_jwt/devise_strategies/password'
|
8
10
|
require 'solidus_jwt/devise_strategies/refresh_token'
|
9
11
|
|
@@ -4,12 +4,16 @@ module SolidusJwt
|
|
4
4
|
# Decode a token generated by SolidusJwt
|
5
5
|
# @see https://github.com/jwt/ruby-jwt
|
6
6
|
#
|
7
|
+
# @example decode a token.
|
8
|
+
# SolidusJwt.decode('abc.123.efg')
|
9
|
+
# #=> [{"sub"=>"1234567890", "name"=>"John Doe", "iat"=>1516239022}, {"alg"=>"HS256", "typ"=>"JWT"}]
|
10
|
+
#
|
7
11
|
# @param token [String] The token to decode
|
8
12
|
# @return [Array<Hash>]
|
9
13
|
#
|
10
14
|
def decode(token)
|
11
15
|
JWT.decode(token, SolidusJwt::Config.jwt_secret, true,
|
12
|
-
|
16
|
+
algorithm: SolidusJwt::Config.jwt_algorithm)
|
13
17
|
end
|
14
18
|
end
|
15
19
|
end
|
@@ -4,6 +4,16 @@ module SolidusJwt
|
|
4
4
|
# Encode a specified payload
|
5
5
|
# @see https://github.com/jwt/ruby-jwt
|
6
6
|
#
|
7
|
+
# @example encode data into token
|
8
|
+
# payload = {
|
9
|
+
# sub: 1,
|
10
|
+
# iat: DateTime.current.to_i,
|
11
|
+
# exp: 1.hour.from_now.to_i
|
12
|
+
# }
|
13
|
+
#
|
14
|
+
# SolidusJwt.encode payload: payload
|
15
|
+
# #=> 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOjEsImlhdCI6MTU4NDEzMjExOCwiZXhwIjoxNTg0MTM1NzE4LCJpc3MiOiJzb2xpZHVzIn0.OKZOGlawx435GdgKp2AGD8SKxW7sqn0h-Ef2qdVSxqQ'
|
16
|
+
#
|
7
17
|
# @param payload [Hash] Attributes to place within the jwt
|
8
18
|
# @param expires_in [Integer] How long until token expires in Seconds (*Optional*).
|
9
19
|
# Note that if no expires at is set, then the token will last forever.
|
@@ -15,12 +25,12 @@ module SolidusJwt
|
|
15
25
|
current_time = Time.current.to_i
|
16
26
|
|
17
27
|
# @see https://github.com/jwt/ruby-jwt#support-for-reserved-claim-names
|
18
|
-
jwt_payload[:exp] ||= current_time + expires_in if expires_in.present?
|
28
|
+
jwt_payload[:exp] ||= current_time + expires_in.to_i if expires_in.present?
|
19
29
|
jwt_payload[:iat] ||= current_time
|
20
30
|
jwt_payload[:iss] ||= 'solidus'
|
21
31
|
|
22
32
|
JWT.encode(jwt_payload, SolidusJwt::Config.jwt_secret,
|
23
|
-
|
33
|
+
SolidusJwt::Config.jwt_algorithm)
|
24
34
|
end
|
25
35
|
end
|
26
36
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SolidusJwt
|
2
|
+
module DeviseStrategies
|
3
|
+
class Base < Devise::Strategies::Authenticatable
|
4
|
+
def valid?
|
5
|
+
valid_grant_type? && valid_params?
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def grant_type
|
11
|
+
params[:grant_type]
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid_grant_type?
|
15
|
+
raise NotImplementedError
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid_params?
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -1,13 +1,7 @@
|
|
1
1
|
module SolidusJwt
|
2
2
|
module DeviseStrategies
|
3
|
-
class Password <
|
4
|
-
def valid?
|
5
|
-
valid_grant_type? && valid_params?
|
6
|
-
end
|
7
|
-
|
3
|
+
class Password < Base
|
8
4
|
def authenticate!
|
9
|
-
resource = mapping.to.find_for_database_authentication(auth_hash)
|
10
|
-
|
11
5
|
block = proc { resource.valid_password?(password) }
|
12
6
|
|
13
7
|
if resource&.valid_for_authentication?(&block)
|
@@ -20,12 +14,12 @@ module SolidusJwt
|
|
20
14
|
|
21
15
|
private
|
22
16
|
|
23
|
-
def
|
24
|
-
|
17
|
+
def resource
|
18
|
+
@resource ||= mapping.to.find_for_database_authentication(auth_hash)
|
25
19
|
end
|
26
20
|
|
27
|
-
def
|
28
|
-
|
21
|
+
def auth_hash
|
22
|
+
{ email: username }
|
29
23
|
end
|
30
24
|
|
31
25
|
def username
|
@@ -1,17 +1,14 @@
|
|
1
1
|
module SolidusJwt
|
2
2
|
module DeviseStrategies
|
3
|
-
class RefreshToken <
|
4
|
-
def valid?
|
5
|
-
valid_grant_type? && valid_params?
|
6
|
-
end
|
7
|
-
|
3
|
+
class RefreshToken < Base
|
8
4
|
def authenticate!
|
9
|
-
resource = SolidusJwt::Token.find_by(auth_hash)
|
10
5
|
return fail!(:invalid) if resource.nil? || resource.user.nil?
|
11
6
|
|
12
7
|
block = proc do
|
13
8
|
# If we honor then mark the refresh token as stale for one time use
|
9
|
+
# rubocop:disable Rails/SkipsModelValidations
|
14
10
|
resource.honor? && resource.update_columns(active: false)
|
11
|
+
# rubocop:enable Rails/SkipsModelValidations
|
15
12
|
end
|
16
13
|
|
17
14
|
if resource.user.valid_for_authentication?(&block)
|
@@ -23,12 +20,12 @@ module SolidusJwt
|
|
23
20
|
|
24
21
|
private
|
25
22
|
|
26
|
-
def
|
27
|
-
|
23
|
+
def resource
|
24
|
+
@resource ||= SolidusJwt::Token.find_by(auth_hash)
|
28
25
|
end
|
29
26
|
|
30
|
-
def
|
31
|
-
|
27
|
+
def auth_hash
|
28
|
+
{ auth_type: :refresh, token: refresh_token }
|
32
29
|
end
|
33
30
|
|
34
31
|
def refresh_token
|
@@ -5,7 +5,7 @@ module SolidusJwt
|
|
5
5
|
# Send back json web token in redirect header
|
6
6
|
if try_spree_current_user
|
7
7
|
response.headers['X-SPREE-TOKEN'] = try_spree_current_user.
|
8
|
-
|
8
|
+
generate_jwt_token(expires_in: SolidusJwt::Config.jwt_expiration)
|
9
9
|
end
|
10
10
|
|
11
11
|
super
|
data/lib/solidus_jwt/engine.rb
CHANGED
@@ -1,22 +1,16 @@
|
|
1
|
+
require 'spree/core'
|
2
|
+
|
1
3
|
module SolidusJwt
|
2
4
|
class Engine < Rails::Engine
|
3
|
-
|
4
|
-
|
5
|
+
include SolidusSupport::EngineExtensions::Decorators
|
6
|
+
|
7
|
+
isolate_namespace ::Spree
|
8
|
+
|
5
9
|
engine_name 'solidus_jwt'
|
6
10
|
|
7
11
|
# use rspec for tests
|
8
12
|
config.generators do |g|
|
9
13
|
g.test_framework :rspec
|
10
14
|
end
|
11
|
-
|
12
|
-
def self.activate
|
13
|
-
decorator_pattern = File.join(__dir__, '../../app/**/*_decorator*.rb')
|
14
|
-
|
15
|
-
Dir.glob(decorator_pattern) do |c|
|
16
|
-
Rails.configuration.cache_classes ? require(c) : load(c)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
config.to_prepare(&method(:activate).to_proc)
|
21
15
|
end
|
22
16
|
end
|
data/lib/solidus_jwt/version.rb
CHANGED
data/solidus_jwt.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative 'lib/solidus_jwt/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'solidus_jwt'
|
5
|
+
s.version = SolidusJwt::VERSION
|
6
|
+
s.summary = 'Add Json Web Tokens to Solidus API'
|
7
|
+
s.description = 'Add Json Web Tokens to Solidus API'
|
8
|
+
s.license = 'BSD-3-Clause'
|
9
|
+
|
10
|
+
s.author = 'Taylor Scott'
|
11
|
+
s.email = 't.skukx@gmail.com'
|
12
|
+
s.homepage = 'https://github.com/skukx/solidus_jwt'
|
13
|
+
|
14
|
+
s.metadata['homepage_uri'] = s.homepage
|
15
|
+
s.metadata['source_code_uri'] = s.homepage
|
16
|
+
|
17
|
+
s.required_ruby_version = Gem::Requirement.new('~> 2.4')
|
18
|
+
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
files = Dir.chdir(__dir__) { `git ls-files -z`.split("\x0") }
|
22
|
+
|
23
|
+
s.files = files.grep_v(%r{^(test|spec|features)/})
|
24
|
+
s.test_files = files.grep(%r{^(test|spec|features)/})
|
25
|
+
s.bindir = "exe"
|
26
|
+
s.executables = files.grep(%r{^exe/}) { |f| File.basename(f) }
|
27
|
+
s.require_paths = ["lib"]
|
28
|
+
|
29
|
+
s.add_dependency 'jwt'
|
30
|
+
s.add_dependency 'solidus_auth_devise'
|
31
|
+
s.add_dependency 'solidus_core', ['>= 2.0.0', '< 3']
|
32
|
+
s.add_dependency 'solidus_support', '~> 0.4.0'
|
33
|
+
|
34
|
+
s.add_development_dependency 'byebug'
|
35
|
+
s.add_development_dependency 'solidus_dev_support'
|
36
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'spree/testing_support/factories/user_factory'
|
3
|
+
|
4
|
+
RSpec.describe SolidusJwt::DeviseStrategies::Password do
|
5
|
+
let(:request) { instance_double('ActionController::Request') }
|
6
|
+
let(:strategy) { described_class.new(nil, :spree_user) }
|
7
|
+
|
8
|
+
let(:params) do
|
9
|
+
{
|
10
|
+
username: user.email,
|
11
|
+
password: password,
|
12
|
+
grant_type: 'password'
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:headers) { {} }
|
17
|
+
let(:user) { FactoryBot.create(:user, password: password) }
|
18
|
+
let(:password) { 'secret' }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(request).to receive(:headers).and_return(:headers)
|
22
|
+
|
23
|
+
allow(strategy).to receive(:request).and_return(request)
|
24
|
+
allow(strategy).to receive(:params).and_return(params)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#valid?' do
|
28
|
+
subject { strategy.valid? }
|
29
|
+
|
30
|
+
it { is_expected.to be true }
|
31
|
+
|
32
|
+
context 'when username is missing' do
|
33
|
+
before { params.delete(:username) }
|
34
|
+
|
35
|
+
it { is_expected.to be false }
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when password is missing' do
|
39
|
+
before { params.delete(:password) }
|
40
|
+
|
41
|
+
it { is_expected.to be false }
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when grant_type is not password' do
|
45
|
+
before { params[:grant_type] = 'invalid' }
|
46
|
+
|
47
|
+
it { is_expected.to be false }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#authenticate!' do
|
52
|
+
subject { strategy.authenticate! }
|
53
|
+
|
54
|
+
it { is_expected.to be :success }
|
55
|
+
|
56
|
+
context 'when auth is invalid' do
|
57
|
+
let(:params) do
|
58
|
+
{
|
59
|
+
username: user.email,
|
60
|
+
password: 'invalid',
|
61
|
+
grant_type: password
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
it { is_expected.to be :failure }
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when user is not valid for authentication' do
|
69
|
+
before do
|
70
|
+
allow_any_instance_of(Spree::User).to receive(:valid_for_authentication?).and_return(false) # rubocop:disable RSpec/AnyInstance
|
71
|
+
end
|
72
|
+
|
73
|
+
it { is_expected.to be :failure }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'spree/testing_support/factories/user_factory'
|
3
|
+
|
4
|
+
RSpec.describe SolidusJwt::DeviseStrategies::RefreshToken do
|
5
|
+
let(:request) { instance_double('ActionController::Request') }
|
6
|
+
let(:strategy) { described_class.new(nil, :spree_user) }
|
7
|
+
|
8
|
+
let(:params) do
|
9
|
+
{
|
10
|
+
refresh_token: token.token,
|
11
|
+
grant_type: 'refresh_token'
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:headers) { {} }
|
16
|
+
let(:user) { FactoryBot.create(:user, password: password) }
|
17
|
+
let(:password) { 'secret' }
|
18
|
+
let(:token) { user.auth_tokens.create! }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(request).to receive(:headers).and_return(:headers)
|
22
|
+
|
23
|
+
allow(strategy).to receive(:request).and_return(request)
|
24
|
+
allow(strategy).to receive(:params).and_return(params)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#valid?' do
|
28
|
+
subject { strategy.valid? }
|
29
|
+
|
30
|
+
it { is_expected.to be true }
|
31
|
+
|
32
|
+
context 'when refresh_token is missing' do
|
33
|
+
before { params.delete(:refresh_token) }
|
34
|
+
|
35
|
+
it { is_expected.to be false }
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when grant_type is not refresh_token' do
|
39
|
+
before { params[:grant_type] = 'invalid' }
|
40
|
+
|
41
|
+
it { is_expected.to be false }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe '#authenticate!' do
|
46
|
+
subject { strategy.authenticate! }
|
47
|
+
|
48
|
+
it { is_expected.to be :success }
|
49
|
+
|
50
|
+
context 'when token is not honorable' do
|
51
|
+
before do
|
52
|
+
allow_any_instance_of(SolidusJwt::Token).to receive(:honor?).and_return false # rubocop:disable RSpec/AnyInstance
|
53
|
+
end
|
54
|
+
|
55
|
+
it { is_expected.to be :failure }
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when user is not valid for authentication' do
|
59
|
+
before do
|
60
|
+
allow_any_instance_of(Spree::User).to receive(:valid_for_authentication?).and_return(false) # rubocop:disable RSpec/AnyInstance
|
61
|
+
end
|
62
|
+
|
63
|
+
it { is_expected.to be :failure }
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when token is used more than once' do
|
67
|
+
before { strategy.authenticate! }
|
68
|
+
|
69
|
+
it { is_expected.to be :failure }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|