jwt-auth 4.2.0 → 5.0.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/.travis.yml +3 -0
- data/Gemfile +3 -0
- data/README.md +119 -18
- data/bin/build +22 -0
- data/bin/release +40 -0
- data/jwt-auth.gemspec +18 -15
- data/lib/jwt/auth.rb +2 -0
- data/lib/jwt/auth/access_token.rb +20 -0
- data/lib/jwt/auth/authenticatable.rb +16 -0
- data/lib/jwt/auth/authentication.rb +63 -22
- data/lib/jwt/auth/configuration.rb +4 -1
- data/lib/jwt/auth/refresh_token.rb +20 -0
- data/lib/jwt/auth/token.rb +49 -41
- data/lib/jwt/auth/version.rb +3 -1
- data/spec/controllers/content_controller_spec.rb +95 -0
- data/spec/controllers/tokens_controller_spec.rb +140 -0
- data/spec/dummy/Rakefile +2 -0
- data/spec/dummy/app/channels/application_cable/channel.rb +2 -0
- data/spec/dummy/app/channels/application_cable/connection.rb +2 -0
- data/spec/dummy/app/controllers/application_controller.rb +6 -1
- data/spec/dummy/app/controllers/content_controller.rb +29 -0
- data/spec/dummy/app/controllers/tokens_controller.rb +53 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/helpers/authentication_helper.rb +2 -0
- data/spec/dummy/app/jobs/application_job.rb +2 -0
- data/spec/dummy/app/mailers/application_mailer.rb +3 -1
- data/spec/dummy/app/models/application_record.rb +2 -0
- data/spec/dummy/app/models/user.rb +3 -6
- data/spec/dummy/bin/bundle +2 -0
- data/spec/dummy/bin/rails +2 -0
- data/spec/dummy/bin/rake +2 -0
- data/spec/dummy/bin/setup +2 -0
- data/spec/dummy/bin/update +2 -0
- data/spec/dummy/bin/yarn +7 -7
- data/spec/dummy/config.ru +2 -0
- data/spec/dummy/config/application.rb +2 -0
- data/spec/dummy/config/boot.rb +3 -1
- data/spec/dummy/config/environment.rb +2 -0
- data/spec/dummy/config/environments/development.rb +3 -1
- data/spec/dummy/config/environments/production.rb +4 -2
- data/spec/dummy/config/environments/test.rb +2 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +2 -0
- data/spec/dummy/config/initializers/assets.rb +2 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +2 -0
- data/spec/dummy/config/initializers/content_security_policy.rb +2 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +2 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +2 -0
- data/spec/dummy/config/initializers/inflections.rb +2 -0
- data/spec/dummy/config/initializers/jwt_auth.rb +9 -2
- data/spec/dummy/config/initializers/mime_types.rb +2 -0
- data/spec/dummy/config/initializers/new_framework_defaults_5_2.rb +2 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +3 -1
- data/spec/dummy/config/puma.rb +5 -3
- data/spec/dummy/config/routes.rb +5 -4
- data/spec/dummy/config/spring.rb +4 -2
- data/spec/dummy/db/migrate/20170726110751_create_users.rb +2 -0
- data/spec/dummy/db/migrate/20170726110825_add_token_version_to_user.rb +2 -0
- data/spec/dummy/db/migrate/20170726112117_add_activated_to_user.rb +2 -0
- data/spec/dummy/db/migrate/20190221100103_add_password_to_user.rb +7 -0
- data/spec/dummy/db/schema.rb +10 -9
- data/spec/jwt/auth/access_token_spec.rb +35 -0
- data/spec/jwt/auth/configuration_spec.rb +36 -0
- data/spec/jwt/auth/refresh_token_spec.rb +35 -0
- data/spec/jwt/auth/token_spec.rb +144 -0
- data/spec/models/user_spec.rb +24 -0
- data/spec/rails_helper.rb +8 -0
- data/spec/spec_helper.rb +51 -53
- data/spec/support/database_cleaner.rb +22 -0
- data/spec/support/matchers/return_token.rb +33 -0
- data/version.yml +1 -0
- metadata +119 -54
- data/spec/authentication_spec.rb +0 -136
- data/spec/configuration_spec.rb +0 -18
- data/spec/dummy/app/controllers/authentication_controller.rb +0 -22
- data/spec/token_spec.rb +0 -125
data/spec/dummy/db/schema.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This file is auto-generated from the current state of the database. Instead
|
2
4
|
# of editing this file, please use the migrations feature of Active Record to
|
3
5
|
# incrementally modify your database, and then regenerate this schema definition.
|
@@ -10,14 +12,13 @@
|
|
10
12
|
#
|
11
13
|
# It's strongly recommended that you check this file into your version control system.
|
12
14
|
|
13
|
-
ActiveRecord::Schema.define(version
|
14
|
-
|
15
|
-
|
16
|
-
t.
|
17
|
-
t.datetime
|
18
|
-
t.
|
19
|
-
t.
|
20
|
-
t.
|
15
|
+
ActiveRecord::Schema.define(:version => 20_190_221_100_103) do
|
16
|
+
create_table 'users', :force => :cascade do |t|
|
17
|
+
t.string 'email'
|
18
|
+
t.datetime 'created_at', :null => false
|
19
|
+
t.datetime 'updated_at', :null => false
|
20
|
+
t.integer 'token_version', :default => 1, :null => false
|
21
|
+
t.boolean 'activated', :default => false
|
22
|
+
t.string 'password'
|
21
23
|
end
|
22
|
-
|
23
24
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_helper'
|
4
|
+
|
5
|
+
RSpec.describe JWT::Auth::AccessToken do
|
6
|
+
##
|
7
|
+
# Configuration
|
8
|
+
#
|
9
|
+
##
|
10
|
+
# Test variables
|
11
|
+
#
|
12
|
+
let(:user) { User.create! :activated => true }
|
13
|
+
|
14
|
+
##
|
15
|
+
# Subject
|
16
|
+
#
|
17
|
+
subject(:token) { described_class.new }
|
18
|
+
|
19
|
+
##
|
20
|
+
# Tests
|
21
|
+
#
|
22
|
+
describe '#type' do
|
23
|
+
it 'returns the correct token type' do
|
24
|
+
expect(subject.type).to eq :access
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#lifetime' do
|
29
|
+
it { is_expected.to respond_to :lifetime }
|
30
|
+
|
31
|
+
it 'returns an integer' do
|
32
|
+
expect(subject.lifetime).to be_a_kind_of Integer
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
require 'rails_helper'
|
6
|
+
|
7
|
+
RSpec.describe JWT::Auth do
|
8
|
+
##
|
9
|
+
# Configuration
|
10
|
+
#
|
11
|
+
JWT::Auth.configure do |config|
|
12
|
+
config.refresh_token_lifetime = 1.year
|
13
|
+
config.access_token_lifetime = 2.hours
|
14
|
+
config.secret = 'mysecret'
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Test variables
|
19
|
+
#
|
20
|
+
|
21
|
+
# Ensure the User model is autoloaded, which initializes the JWT::Auth.model configuration entry
|
22
|
+
before { User }
|
23
|
+
|
24
|
+
##
|
25
|
+
# Subject
|
26
|
+
#
|
27
|
+
subject { JWT::Auth }
|
28
|
+
|
29
|
+
##
|
30
|
+
# Tests
|
31
|
+
#
|
32
|
+
it { is_expected.to have_attributes :refresh_token_lifetime => 1.year,
|
33
|
+
:access_token_lifetime => 2.hours,
|
34
|
+
:secret => 'mysecret',
|
35
|
+
:model => 'User' }
|
36
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_helper'
|
4
|
+
|
5
|
+
RSpec.describe JWT::Auth::RefreshToken do
|
6
|
+
##
|
7
|
+
# Configuration
|
8
|
+
#
|
9
|
+
##
|
10
|
+
# Test variables
|
11
|
+
#
|
12
|
+
let(:user) { User.create! :activated => true }
|
13
|
+
|
14
|
+
##
|
15
|
+
# Subject
|
16
|
+
#
|
17
|
+
subject(:token) { described_class.new }
|
18
|
+
|
19
|
+
##
|
20
|
+
# Tests
|
21
|
+
#
|
22
|
+
describe '#type' do
|
23
|
+
it 'returns the correct token type' do
|
24
|
+
expect(subject.type).to eq :refresh
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#lifetime' do
|
29
|
+
it { is_expected.to respond_to :lifetime }
|
30
|
+
|
31
|
+
it 'returns an integer' do
|
32
|
+
expect(subject.lifetime).to be_a_kind_of Integer
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_helper'
|
4
|
+
|
5
|
+
RSpec.describe JWT::Auth::Token do
|
6
|
+
##
|
7
|
+
# Configuration
|
8
|
+
#
|
9
|
+
##
|
10
|
+
# Test variables
|
11
|
+
#
|
12
|
+
let(:user) { User.create! :activated => true }
|
13
|
+
|
14
|
+
##
|
15
|
+
# Subject
|
16
|
+
#
|
17
|
+
subject(:token) { described_class.new }
|
18
|
+
|
19
|
+
##
|
20
|
+
# Tests
|
21
|
+
#
|
22
|
+
describe 'properties' do
|
23
|
+
it { is_expected.to respond_to :issued_at }
|
24
|
+
it { is_expected.to respond_to :subject }
|
25
|
+
it { is_expected.to respond_to :version }
|
26
|
+
|
27
|
+
it { is_expected.to have_attributes :issued_at => nil, :subject => nil, :version => nil }
|
28
|
+
|
29
|
+
describe 'constructor' do
|
30
|
+
subject(:token) { described_class.new :issued_at => 'foo', :subject => 'bar', :version => 'bat' }
|
31
|
+
|
32
|
+
it { is_expected.to have_attributes :issued_at => 'foo', :subject => 'bar', :version => 'bat' }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#valid?' do
|
37
|
+
before do
|
38
|
+
# Override not implemented methods for test purposes
|
39
|
+
allow_any_instance_of(described_class).to receive(:type).and_return :access
|
40
|
+
allow_any_instance_of(described_class).to receive(:lifetime).and_return 2.hours.to_i
|
41
|
+
end
|
42
|
+
|
43
|
+
subject(:token) { described_class.new :subject => user,
|
44
|
+
:issued_at => Time.now.to_i,
|
45
|
+
:version => user.token_version }
|
46
|
+
|
47
|
+
it { is_expected.to be_valid }
|
48
|
+
|
49
|
+
context 'when the subject is nil' do
|
50
|
+
before { token.subject = nil }
|
51
|
+
|
52
|
+
it { is_expected.not_to be_valid }
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when the subject is destroyed' do
|
56
|
+
before { user.destroy }
|
57
|
+
|
58
|
+
it { is_expected.not_to be_valid }
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'when issued_at is nil' do
|
62
|
+
before { token.issued_at = nil }
|
63
|
+
|
64
|
+
it { is_expected.not_to be_valid }
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when version is nil' do
|
68
|
+
before { token.version = nil }
|
69
|
+
|
70
|
+
it { is_expected.not_to be_valid }
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'when token_version is incremented' do
|
74
|
+
# Explicitly call `token` to initialize it with the old token_version
|
75
|
+
before { token; user.increment! :token_version }
|
76
|
+
|
77
|
+
it { is_expected.not_to be_valid }
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'when the token has expired' do
|
81
|
+
before { token.issued_at = JWT::Auth.access_token_lifetime.ago.to_i }
|
82
|
+
|
83
|
+
it { is_expected.not_to be_valid }
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'when the issued_at is in the future' do
|
87
|
+
before { token.issued_at = 1.year.from_now.to_i }
|
88
|
+
|
89
|
+
it { is_expected.not_to be_valid }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe '#to_jwt' do
|
94
|
+
before do
|
95
|
+
# Override not implemented methods for test purposes
|
96
|
+
allow_any_instance_of(described_class).to receive(:type).and_return :access
|
97
|
+
end
|
98
|
+
|
99
|
+
let(:token) { described_class.new :subject => user }
|
100
|
+
subject(:payload) { JWT.decode(token.to_jwt, JWT::Auth.secret).first }
|
101
|
+
|
102
|
+
it { is_expected.to eq 'iat' => Time.now.to_i, 'sub' => user.id, 'ver' => user.token_version, 'typ' => 'access' }
|
103
|
+
end
|
104
|
+
|
105
|
+
describe '.from_jwt' do
|
106
|
+
let(:jwt) { JWT.encode({ :iat => Time.now.to_i, :sub => user.id, :ver => user.token_version, :typ => type }, JWT::Auth.secret) }
|
107
|
+
let(:type) { :access }
|
108
|
+
subject(:token) { described_class.from_jwt jwt }
|
109
|
+
|
110
|
+
it { is_expected.to have_attributes :issued_at => a_kind_of(Integer), :subject => user, :version => user.token_version }
|
111
|
+
|
112
|
+
context 'when the jwt cannot be decoded' do
|
113
|
+
let(:jwt) { 'rubbish' }
|
114
|
+
|
115
|
+
it { is_expected.to be_nil }
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'when the typ payload parameter is nil' do
|
119
|
+
let(:type) { nil }
|
120
|
+
|
121
|
+
it { is_expected.to be_nil }
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'when the typ payload parameter is "access"' do
|
125
|
+
let(:type) { :access }
|
126
|
+
|
127
|
+
it { is_expected.to be_an_instance_of JWT::Auth::AccessToken }
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'when the typ payload parameter is "refresh"' do
|
131
|
+
let(:type) { :refresh }
|
132
|
+
|
133
|
+
it { is_expected.to be_an_instance_of JWT::Auth::RefreshToken }
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'calls the User#find_by_token method' do
|
137
|
+
expect(User).to receive(:find_by_token)
|
138
|
+
.with :id => user.id,
|
139
|
+
:token_version => user.token_version
|
140
|
+
|
141
|
+
described_class.from_jwt jwt
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_helper'
|
4
|
+
|
5
|
+
RSpec.describe User do
|
6
|
+
##
|
7
|
+
# Configuration
|
8
|
+
#
|
9
|
+
##
|
10
|
+
# Test variables
|
11
|
+
#
|
12
|
+
##
|
13
|
+
# Subject
|
14
|
+
#
|
15
|
+
##
|
16
|
+
# Tests
|
17
|
+
#
|
18
|
+
it { is_expected.to validate_presence_of :token_version }
|
19
|
+
it { is_expected.to respond_to :find_by_token }
|
20
|
+
|
21
|
+
it 'sets the JWT::Auth configuration model entry to User' do
|
22
|
+
expect(JWT::Auth.model).to eq described_class.to_s
|
23
|
+
end
|
24
|
+
end
|
data/spec/rails_helper.rb
CHANGED
@@ -6,6 +6,7 @@ abort('The Rails environment is running in production mode!') if Rails.env.produ
|
|
6
6
|
require 'spec_helper'
|
7
7
|
require 'rspec/rails'
|
8
8
|
# Add additional requires below this line. Rails is not loaded until this point!
|
9
|
+
require 'shoulda/matchers'
|
9
10
|
|
10
11
|
require 'dummy/config/environment'
|
11
12
|
# ENV['RAILS_ROOT'] ||= File.join File.dirname(__FILE__), 'spec', 'dummy'
|
@@ -48,3 +49,10 @@ RSpec.configure do |config|
|
|
48
49
|
# arbitrary gems may also be filtered via:
|
49
50
|
# config.filter_gems_from_backtrace("gem name")
|
50
51
|
end
|
52
|
+
|
53
|
+
Shoulda::Matchers.configure do |config|
|
54
|
+
config.integrate do |with|
|
55
|
+
with.test_framework :rspec
|
56
|
+
with.library :rails
|
57
|
+
end
|
58
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -57,57 +57,55 @@ RSpec.configure do |config|
|
|
57
57
|
# triggering implicit auto-inclusion in groups with matching metadata.
|
58
58
|
config.shared_context_metadata_behavior = :apply_to_host_groups
|
59
59
|
|
60
|
-
# The settings below are suggested to provide a good initial experience
|
61
|
-
# with RSpec, but feel free to customize to your heart's content.
|
62
|
-
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
|
69
|
-
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
|
74
|
-
|
75
|
-
#
|
76
|
-
#
|
77
|
-
# - http://
|
78
|
-
# - http://
|
79
|
-
#
|
80
|
-
|
81
|
-
|
82
|
-
#
|
83
|
-
#
|
84
|
-
|
85
|
-
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
|
100
|
-
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
|
106
|
-
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
Kernel.srand config.seed
|
112
|
-
=end
|
60
|
+
# The settings below are suggested to provide a good initial experience
|
61
|
+
# with RSpec, but feel free to customize to your heart's content.
|
62
|
+
# # This allows you to limit a spec run to individual examples or groups
|
63
|
+
# # you care about by tagging them with `:focus` metadata. When nothing
|
64
|
+
# # is tagged with `:focus`, all examples get run. RSpec also provides
|
65
|
+
# # aliases for `it`, `describe`, and `context` that include `:focus`
|
66
|
+
# # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
67
|
+
# config.filter_run_when_matching :focus
|
68
|
+
#
|
69
|
+
# # Allows RSpec to persist some state between runs in order to support
|
70
|
+
# # the `--only-failures` and `--next-failure` CLI options. We recommend
|
71
|
+
# # you configure your source control system to ignore this file.
|
72
|
+
# config.example_status_persistence_file_path = "spec/examples.txt"
|
73
|
+
#
|
74
|
+
# # Limits the available syntax to the non-monkey patched syntax that is
|
75
|
+
# # recommended. For more details, see:
|
76
|
+
# # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
77
|
+
# # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
78
|
+
# # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
79
|
+
# config.disable_monkey_patching!
|
80
|
+
#
|
81
|
+
# # This setting enables warnings. It's recommended, but in some cases may
|
82
|
+
# # be too noisy due to issues in dependencies.
|
83
|
+
# config.warnings = true
|
84
|
+
#
|
85
|
+
# # Many RSpec users commonly either run the entire suite or an individual
|
86
|
+
# # file, and it's useful to allow more verbose output when running an
|
87
|
+
# # individual spec file.
|
88
|
+
# if config.files_to_run.one?
|
89
|
+
# # Use the documentation formatter for detailed output,
|
90
|
+
# # unless a formatter has already been configured
|
91
|
+
# # (e.g. via a command-line flag).
|
92
|
+
# config.default_formatter = 'doc'
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# # Print the 10 slowest examples and example groups at the
|
96
|
+
# # end of the spec run, to help surface which specs are running
|
97
|
+
# # particularly slow.
|
98
|
+
# config.profile_examples = 10
|
99
|
+
#
|
100
|
+
# # Run specs in random order to surface order dependencies. If you find an
|
101
|
+
# # order dependency and want to debug it, you can fix the order by providing
|
102
|
+
# # the seed, which is printed after each run.
|
103
|
+
# # --seed 1234
|
104
|
+
# config.order = :random
|
105
|
+
#
|
106
|
+
# # Seed global randomization in this process using the `--seed` CLI option.
|
107
|
+
# # Setting this allows you to use `--seed` to deterministically reproduce
|
108
|
+
# # test failures related to randomization by passing the same `--seed` value
|
109
|
+
# # as the one that triggered the failure.
|
110
|
+
# Kernel.srand config.seed
|
113
111
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'database_cleaner'
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
# before the entire test suite runs, clear the test database out completely
|
7
|
+
config.before(:suite) do
|
8
|
+
DatabaseCleaner.clean
|
9
|
+
end
|
10
|
+
|
11
|
+
# Default strategy is transaction (very fast)
|
12
|
+
config.before do
|
13
|
+
DatabaseCleaner.strategy = :transaction
|
14
|
+
end
|
15
|
+
|
16
|
+
config.before do
|
17
|
+
DatabaseCleaner.start
|
18
|
+
end
|
19
|
+
config.after do
|
20
|
+
DatabaseCleaner.clean
|
21
|
+
end
|
22
|
+
end
|