jwt-auth 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a874a275ba2aca5369cd936de7858c338e234cfe
4
- data.tar.gz: b4a5a6fbc4143376145497ba0790f9e7a55334df
3
+ metadata.gz: d74cc6625139bc3902a071d6d874459689d5d760
4
+ data.tar.gz: 486adce1dd96a6eb8131de2ae8f423ed0c5bea88
5
5
  SHA512:
6
- metadata.gz: efb36f4092b5c9e0b3810c3619991e3f51cfb318af6182aa01d6ad18c8a7116766fc6bcd59d52f24b0132c6ccf4bc7d51e0420592ea7b78e213cd4f67e521cdb
7
- data.tar.gz: b0d0f600130e4db273164e0c02ecc42e6983ffc5dbc2b62cf796c2ba3965c407e037696782af05553628d67fb553202dd57690a4933e7176d815246bfcc5b51f
6
+ metadata.gz: d0bd4f187aa11e758844a12fb84dc7fc05cb3f0f9c402a83968412a4eafa1f38ac93249483cbf63a02f56df54a8834b676e1aef67a5f9ab9138be089efc498ec
7
+ data.tar.gz: f2da60f7133afd87c05d5474575f4e20a7622ff7a48890ec8971e071253ae5109dde7eecf143ce327009cb9e9d1f4e8bf0bda66c9db9d6a0ebad2c240e2f0e0d
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
File without changes
data/README.md CHANGED
@@ -21,11 +21,12 @@ Or install it yourself as:
21
21
  Create an initializer:
22
22
 
23
23
  ```ruby
24
- JwtAuth.configure do |config|
24
+ JWT::Auth.configure do |config|
25
25
  ##
26
- # Token lifetime (in hours)
26
+ # Token lifetime
27
27
  #
28
- config.token_lifetime = 24
28
+ config.token_lifetime = 24.hours
29
+
29
30
  ##
30
31
  # JWT secret
31
32
  #
@@ -41,6 +42,17 @@ class User < ApplicationRecord
41
42
  end
42
43
  ```
43
44
 
45
+ Add a `token_version` field to your user model:
46
+
47
+ ```ruby
48
+ class AddTokenVersionToUser < ActiveRecord::Migration[5.0]
49
+ def change
50
+ add_column :users, :token_version, :integer, :null => false, :default => 1
51
+ end
52
+ end
53
+
54
+ ```
55
+
44
56
  Include controller methods in your `ApplicationController`:
45
57
 
46
58
  ```ruby
@@ -3,3 +3,7 @@
3
3
  require 'jwt'
4
4
 
5
5
  require 'jwt/auth/version'
6
+ require 'jwt/auth/configuration'
7
+ require 'jwt/auth/authenticatable'
8
+ require 'jwt/auth/authentication'
9
+ require 'jwt/auth/token'
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module JWT
6
+ module Auth
7
+ ##
8
+ # User model methods
9
+ #
10
+ module Authenticatable
11
+ extend ActiveSupport::Concern
12
+
13
+ included do
14
+ JWT::Auth.configure do |config|
15
+ config.model = const_get name
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jwt/auth/configuration'
4
+
5
+ module JWT
6
+ module Auth
7
+ ##
8
+ # Controller methods
9
+ #
10
+ module Authentication
11
+ ##
12
+ # Current user helper
13
+ #
14
+ def current_user
15
+ token&.subject
16
+ end
17
+
18
+ ##
19
+ # Authenticate a request
20
+ #
21
+ def authenticate_user
22
+ return head :unauthorized unless token&.valid?
23
+
24
+ # Regenerate token (renews expiration date)
25
+ add_token_to_response
26
+ end
27
+
28
+ ##
29
+ # Add JWT header to response
30
+ #
31
+ def add_token_to_response
32
+ token.renew!
33
+ response.headers['Authorization'] = "Bearer #{token.to_jwt}"
34
+ end
35
+
36
+ protected
37
+
38
+ ##
39
+ # Extract JWT from request
40
+ #
41
+ def token
42
+ return @token if @token
43
+
44
+ header = request.env['HTTP_AUTHORIZATION']
45
+ return nil unless header
46
+
47
+ token = header.scan(/Bearer (.*)$/).flatten.last
48
+ return nil unless token
49
+
50
+ @token = JWT::Auth::Token.from_token token
51
+ rescue JWT::DecodeError
52
+ nil
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JWT
4
+ module Auth
5
+ class << self
6
+ attr_accessor :model, :secret, :token_lifetime
7
+
8
+ def configure
9
+ yield JWT::Auth
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/numeric/time'
4
+
5
+ require 'jwt/auth/configuration'
6
+
7
+ module JWT
8
+ module Auth
9
+ ##
10
+ # In-memory representation of JWT
11
+ #
12
+ class Token
13
+ attr_accessor :expiration, :subject
14
+
15
+ def valid?
16
+ subject && Time.at(expiration).future?
17
+ end
18
+
19
+ def renew!
20
+ self.expiration = nil
21
+ end
22
+
23
+ def to_jwt
24
+ payload = {
25
+ :exp => expiration || JWT::Auth.token_lifetime.from_now.to_i,
26
+ :sub => subject.id,
27
+ :ver => subject.token_version
28
+ }
29
+ JWT.encode payload, JWT::Auth.secret
30
+ end
31
+
32
+ def self.from_user(subject)
33
+ token = JWT::Auth::Token.new
34
+ token.subject = subject
35
+
36
+ token
37
+ end
38
+
39
+ def self.from_token(token)
40
+ payload = JWT.decode(token, JWT::Auth.secret).first
41
+
42
+ token = JWT::Auth::Token.new
43
+ token.expiration = payload['exp']
44
+ token.subject = JWT::Auth.model.find_by :id => payload['sub'], :token_version => payload['ver']
45
+
46
+ token
47
+ end
48
+ end
49
+ end
50
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module JWT
4
4
  module Auth
5
- VERSION = '1.0.0'
5
+ VERSION = '1.1.0'
6
6
  end
7
7
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'support/dummy_user'
4
+
5
+ RSpec.describe JWT::Auth do
6
+ it 'configures correctly' do
7
+ JWT::Auth.configure do |config|
8
+ config.token_lifetime = 666
9
+ config.secret = 'mysecret'
10
+ end
11
+
12
+ expect(subject.token_lifetime).to eq 666
13
+ expect(subject.secret).to eq 'mysecret'
14
+ expect(subject.model).to eq DummyUser
15
+ end
16
+ end
@@ -1,4 +1,107 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rack/test'
4
3
  require 'jwt/auth'
4
+
5
+ # This file was generated by the `rspec --init` command. Conventionally, all
6
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
7
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
8
+ # this file to always be loaded, without a need to explicitly require it in any
9
+ # files.
10
+ #
11
+ # Given that it is always loaded, you are encouraged to keep this file as
12
+ # light-weight as possible. Requiring heavyweight dependencies from this file
13
+ # will add to the boot time of your test suite on EVERY test run, even for an
14
+ # individual file that may not need all of that loaded. Instead, consider making
15
+ # a separate helper file that requires the additional dependencies and performs
16
+ # the additional setup, and require it from the spec files that actually need
17
+ # it.
18
+ #
19
+ # The `.rspec` file also contains a few flags that are not defaults but that
20
+ # users commonly want.
21
+ #
22
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
23
+ RSpec.configure do |config|
24
+ # rspec-expectations config goes here. You can use an alternate
25
+ # assertion/expectation library such as wrong or the stdlib/minitest
26
+ # assertions if you prefer.
27
+ config.expect_with :rspec do |expectations|
28
+ # This option will default to `true` in RSpec 4. It makes the `description`
29
+ # and `failure_message` of custom matchers include text for helper methods
30
+ # defined using `chain`, e.g.:
31
+ # be_bigger_than(2).and_smaller_than(4).description
32
+ # # => "be bigger than 2 and smaller than 4"
33
+ # ...rather than:
34
+ # # => "be bigger than 2"
35
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
36
+ end
37
+
38
+ # rspec-mocks config goes here. You can use an alternate test double
39
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
40
+ config.mock_with :rspec do |mocks|
41
+ # Prevents you from mocking or stubbing a method that does not exist on
42
+ # a real object. This is generally recommended, and will default to
43
+ # `true` in RSpec 4.
44
+ mocks.verify_partial_doubles = true
45
+ end
46
+
47
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
48
+ # have no way to turn it off -- the option exists only for backwards
49
+ # compatibility in RSpec 3). It causes shared context metadata to be
50
+ # inherited by the metadata hash of host groups and examples, rather than
51
+ # triggering implicit auto-inclusion in groups with matching metadata.
52
+ config.shared_context_metadata_behavior = :apply_to_host_groups
53
+
54
+ # The settings below are suggested to provide a good initial experience
55
+ # with RSpec, but feel free to customize to your heart's content.
56
+ =begin
57
+ # This allows you to limit a spec run to individual examples or groups
58
+ # you care about by tagging them with `:focus` metadata. When nothing
59
+ # is tagged with `:focus`, all examples get run. RSpec also provides
60
+ # aliases for `it`, `describe`, and `context` that include `:focus`
61
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
62
+ config.filter_run_when_matching :focus
63
+
64
+ # Allows RSpec to persist some state between runs in order to support
65
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
66
+ # you configure your source control system to ignore this file.
67
+ config.example_status_persistence_file_path = "spec/examples.txt"
68
+
69
+ # Limits the available syntax to the non-monkey patched syntax that is
70
+ # recommended. For more details, see:
71
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
72
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
73
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
74
+ config.disable_monkey_patching!
75
+
76
+ # This setting enables warnings. It's recommended, but in some cases may
77
+ # be too noisy due to issues in dependencies.
78
+ config.warnings = true
79
+
80
+ # Many RSpec users commonly either run the entire suite or an individual
81
+ # file, and it's useful to allow more verbose output when running an
82
+ # individual spec file.
83
+ if config.files_to_run.one?
84
+ # Use the documentation formatter for detailed output,
85
+ # unless a formatter has already been configured
86
+ # (e.g. via a command-line flag).
87
+ config.default_formatter = 'doc'
88
+ end
89
+
90
+ # Print the 10 slowest examples and example groups at the
91
+ # end of the spec run, to help surface which specs are running
92
+ # particularly slow.
93
+ config.profile_examples = 10
94
+
95
+ # Run specs in random order to surface order dependencies. If you find an
96
+ # order dependency and want to debug it, you can fix the order by providing
97
+ # the seed, which is printed after each run.
98
+ # --seed 1234
99
+ config.order = :random
100
+
101
+ # Seed global randomization in this process using the `--seed` CLI option.
102
+ # Setting this allows you to use `--seed` to deterministically reproduce
103
+ # test failures related to randomization by passing the same `--seed` value
104
+ # as the one that triggered the failure.
105
+ Kernel.srand config.seed
106
+ =end
107
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jwt/auth'
4
+
5
+ class DummyUser
6
+ include JWT::Auth::Authenticatable
7
+
8
+ attr_accessor :id, :token_version
9
+
10
+ def initialize(params = {})
11
+ self.id = params[:id] || Random.rand(1000)
12
+ self.token_version = params[:token_version] || 1
13
+ end
14
+
15
+ def self.find_by(params)
16
+ DummyUser.new params
17
+ end
18
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'support/dummy_user'
4
+
5
+ RSpec.describe JWT::Auth::Token do
6
+ before :each do
7
+ JWT::Auth.configure do |config|
8
+ config.token_lifetime = 24.hours
9
+
10
+ config.secret = 'mysecret'
11
+ end
12
+ end
13
+
14
+ let(:user) { DummyUser.new }
15
+
16
+ describe 'properties' do
17
+ let(:token) { token = JWT::Auth::Token.from_user user }
18
+
19
+ it 'has an expiration' do
20
+ expect(token).to respond_to :expiration
21
+ expect(token.expiration).to be_nil
22
+ end
23
+
24
+ it 'has a subject' do
25
+ expect(token).to respond_to :subject
26
+ expect(token.subject).to eq user
27
+ end
28
+ end
29
+
30
+ describe 'from token' do
31
+ let(:jwt) do
32
+ payload = {
33
+ :exp => JWT::Auth.token_lifetime.from_now.to_i,
34
+ :sub => user.id,
35
+ :ver => user.token_version
36
+ }
37
+ JWT.encode payload, JWT::Auth.secret
38
+ end
39
+
40
+ let(:token) { JWT::Auth::Token.from_token jwt }
41
+
42
+ it 'matches expiration' do
43
+ expect(token.expiration).to eq JWT::Auth.token_lifetime.from_now.to_i
44
+ end
45
+
46
+ it 'matches subject' do
47
+ expect(token.subject.id).to eq user.id
48
+ expect(token.subject.token_version).to eq user.token_version
49
+ end
50
+ end
51
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jwt-auth
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Florian Dejonckheere
@@ -103,15 +103,23 @@ extensions: []
103
103
  extra_rdoc_files: []
104
104
  files:
105
105
  - ".gitignore"
106
+ - ".rspec"
106
107
  - ".rubocop.yml"
107
108
  - Gemfile
108
- - LICENSE
109
+ - LICENSE.md
109
110
  - README.md
110
111
  - Rakefile
111
112
  - jwt-auth.gemspec
112
113
  - lib/jwt/auth.rb
114
+ - lib/jwt/auth/authenticatable.rb
115
+ - lib/jwt/auth/authentication.rb
116
+ - lib/jwt/auth/configuration.rb
117
+ - lib/jwt/auth/token.rb
113
118
  - lib/jwt/auth/version.rb
119
+ - spec/configuration_spec.rb
114
120
  - spec/spec_helper.rb
121
+ - spec/support/dummy_user.rb
122
+ - spec/token_spec.rb
115
123
  homepage: https://github.com/floriandejonckheere/jwt-auth
116
124
  licenses:
117
125
  - MIT
@@ -137,4 +145,7 @@ signing_key:
137
145
  specification_version: 4
138
146
  summary: JWT-based authentication for Rails API without Devise
139
147
  test_files:
148
+ - spec/configuration_spec.rb
140
149
  - spec/spec_helper.rb
150
+ - spec/support/dummy_user.rb
151
+ - spec/token_spec.rb