api_guard 0.5.0 → 0.6.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
  SHA256:
3
- metadata.gz: 16c29cefc6fd22302a55e87e7ec5f52d6fc4936640ee190cad692646cbe37cc7
4
- data.tar.gz: daa21cfa0052dc034fc5e5c18c380a9ee3eac72e3d1490db895798dc2f31c9f7
3
+ metadata.gz: 664b5fd08385f8cb690e350127d83bdeb8228264ccf179bc2a4242841e27e114
4
+ data.tar.gz: 6c94a5a7867c4ce5aa186f4f18fbfd4706b654818de791e23b8d83827e58fcf1
5
5
  SHA512:
6
- metadata.gz: 6daaca97ec4dd5d3c4f9938642edc77b2fc25a8854985d6b305e4ce1e83b20dd52b2056fed4e0e9d8b6e14f9f0049391afe42c40f455983d9386556fc1bca37c
7
- data.tar.gz: a3feb57d806c7581a6679833a2cb0f41328e8b9a9077acd5504898173527b7ae30778d846efbbac4106d52abea65c35300e6714f2aa0cc2cb3016e1dd2b1eb2f
6
+ metadata.gz: 4d5b0b0145c8c2c82a4a679c2ece7249e397ec3ad497dc9336388fff6d6830e76f70078336a50b790ace86f374967840c99535b86989bb2bec570d30fbe5b912
7
+ data.tar.gz: e90af09ed86e505eb7a38f489f3347fbfc9f906f7d141748c4c4bc28365cf165cbfa668832eceef29e744dcb9ea929e4b568181730cd6337023183b64cd5cd40
data/README.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # API Guard
2
2
 
3
3
  [![Version](https://img.shields.io/gem/v/api_guard.svg?color=green)](https://rubygems.org/gems/api_guard)
4
- [![Build Status](https://github.com/Gokul595/api_guard/workflows/build/badge.svg?branch=master)](https://github.com/Gokul595/api_guard/actions?query=workflow%3Abuild)
4
+ [![Build Status](https://github.com/Gokul595/api_guard/workflows/build-master/badge.svg?branch=master)](https://github.com/Gokul595/api_guard/actions?query=workflow%3Abuild-master)
5
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/ced3e74a26a66ed915cb/maintainability)](https://codeclimate.com/github/Gokul595/api_guard/maintainability)
6
- [![Test Coverage](https://api.codeclimate.com/v1/badges/ced3e74a26a66ed915cb/test_coverage)](https://codeclimate.com/github/Gokul595/api_guard/test_coverage)
7
6
 
8
7
 
9
8
  [JSON Web Token (JWT)](https://jwt.io/) based authentication solution with token refreshing & blacklisting for APIs
@@ -203,6 +202,8 @@ To authenticate the API request just add this before_action in the controller:
203
202
  before_action :authenticate_and_set_user
204
203
  ```
205
204
 
205
+ >**Note:** It is possible to authenticate with more than one resource, e.g. `authenticate_and_set_user_or_admin` will permit tokens issued for users or admins.
206
+
206
207
  Send the access token got in sign in API in the Authorization header in the API request as below.
207
208
  Also, make sure you add "Bearer" before the access token in the header value.
208
209
 
@@ -423,7 +424,7 @@ To include token refreshing in your application you need to create a table to st
423
424
  Use below command to create a model `RefeshToken` with columns to store the token and the user reference
424
425
 
425
426
  ```bash
426
- $ rails generate model refresh_token token:string:uniq user:references
427
+ $ rails generate model refresh_token token:string:uniq user:references expire_at:datetime
427
428
  ```
428
429
 
429
430
  Then, run migration to create the `refresh_tokens` table
@@ -452,11 +453,11 @@ api_guard_associations refresh_token: 'refresh_tokens', blacklisted_token: 'blac
452
453
 
453
454
  ### Token blacklisting
454
455
 
455
- To include token blacklisting in your application you need to create a table to store the refresh tokens. This will be
456
+ To include token blacklisting in your application you need to create a table to store the blacklisted tokens. This will be
456
457
  used to blacklist a JWT access token from future use. The access token will be blacklisted on successful sign out of the
457
458
  resource.
458
459
 
459
- Use below command to create a model `RefeshToken` with columns to store the token and the user reference
460
+ Use below command to create a model `BlacklistedToken` with columns to store the token and the user reference
460
461
 
461
462
  ```bash
462
463
  $ rails generate model blacklisted_token token:string user:references expire_at:datetime
@@ -9,8 +9,8 @@ module ApiGuard
9
9
  method_name = name.to_s
10
10
 
11
11
  if method_name.start_with?('authenticate_and_set_')
12
- resource_name = method_name.split('authenticate_and_set_')[1]
13
- authenticate_and_set_resource(resource_name)
12
+ resource_names = method_name.split('authenticate_and_set_')[1].split('_or_')
13
+ authenticate_and_set_resources(resource_names)
14
14
  else
15
15
  super
16
16
  end
@@ -20,9 +20,9 @@ module ApiGuard
20
20
  method_name.to_s.start_with?('authenticate_and_set_') || super
21
21
  end
22
22
 
23
- # Authenticate the JWT token and set resource
24
- def authenticate_and_set_resource(resource_name)
25
- @resource_name = resource_name
23
+ # Authenticate the JWT token and set resources
24
+ def authenticate_and_set_resources(resource_names)
25
+ @resource_names = resource_names
26
26
 
27
27
  @token = request.headers['Authorization']&.split('Bearer ')&.last
28
28
  return render_error(401, message: I18n.t('api_guard.access_token.missing')) unless @token
@@ -58,7 +58,7 @@ module ApiGuard
58
58
  # Defines "current_{{resource_name}}" method and "@current_{{resource_name}}" instance variable
59
59
  # that returns "resource" value
60
60
  def define_current_resource_accessors(resource)
61
- self.class.send(:define_method, "current_#{@resource_name}") do
61
+ define_singleton_method("current_#{@resource_name}") do
62
62
  instance_variable_get("@current_#{@resource_name}") ||
63
63
  instance_variable_set("@current_#{@resource_name}", resource)
64
64
  end
@@ -72,15 +72,25 @@ module ApiGuard
72
72
  def authenticate_token
73
73
  return unless decode_token
74
74
 
75
+ @resource_name = set_resource_name_from_token(@resource_names)
76
+ return if @resource_name.nil?
77
+
75
78
  resource = find_resource_from_token(@resource_name.classify.constantize)
76
79
 
77
80
  if resource && valid_issued_at?(resource) && !blacklisted?(resource)
78
81
  define_current_resource_accessors(resource)
79
- else
80
- render_error(401, message: I18n.t('api_guard.access_token.invalid'))
81
82
  end
82
83
  end
83
84
 
85
+ def set_resource_name_from_token(resource_names)
86
+ resource_names.each do |name|
87
+ resource_id = @decoded_token[:"#{name}_id"]
88
+ return name if resource_id.present?
89
+ end
90
+
91
+ return nil
92
+ end
93
+
84
94
  def find_resource_from_token(resource_class)
85
95
  resource_id = @decoded_token[:"#{@resource_name}_id"]
86
96
  return if resource_id.blank?
@@ -34,7 +34,8 @@ module ApiGuard
34
34
  # Also, create refresh token if enabled for the resource.
35
35
  #
36
36
  # This creates expired JWT token if the argument 'expired_token' is true which can be used for testing.
37
- def jwt_and_refresh_token(resource, resource_name, expired_token = false)
37
+ # This creates expired refresh token if the argument 'expired_refresh_token' is true which can be used for testing.
38
+ def jwt_and_refresh_token(resource, resource_name, expired_token = false, expired_refresh_token = false)
38
39
  payload = {
39
40
  "#{resource_name}_id": resource.id,
40
41
  exp: expired_token ? token_issued_at : token_expire_at,
@@ -44,7 +45,7 @@ module ApiGuard
44
45
  # Add custom data in the JWT token payload
45
46
  payload.merge!(resource.jwt_token_payload) if resource.respond_to?(:jwt_token_payload)
46
47
 
47
- [encode(payload), new_refresh_token(resource)]
48
+ [encode(payload), new_refresh_token(resource, expired_refresh_token)]
48
49
  end
49
50
 
50
51
  # Create tokens and set response headers
@@ -4,6 +4,11 @@ module ApiGuard
4
4
  module JwtAuth
5
5
  # Common module for refresh token functionality
6
6
  module RefreshJwtToken
7
+
8
+ def refresh_token_expire_at
9
+ @refresh_token_expire_at ||= (Time.now.utc + ApiGuard.refresh_token_validity)
10
+ end
11
+
7
12
  def refresh_token_association(resource)
8
13
  resource.class.refresh_token_association
9
14
  end
@@ -18,7 +23,7 @@ module ApiGuard
18
23
  end
19
24
 
20
25
  def find_refresh_token_of(resource, refresh_token)
21
- refresh_tokens_for(resource).find_by_token(refresh_token)
26
+ refresh_tokens_for(resource).where(token: refresh_token).where('expire_at IS NULL OR expire_at > ?', Time.now.utc).first
22
27
  end
23
28
 
24
29
  # Generate and return unique refresh token for the resource
@@ -30,10 +35,11 @@ module ApiGuard
30
35
  end
31
36
 
32
37
  # Create a new refresh_token for the current resource
33
- def new_refresh_token(resource)
38
+ # This creates expired refresh_token if the argument 'expired_refresh_token' is true which can be used for testing.
39
+ def new_refresh_token(resource, expired_refresh_token = false)
34
40
  return unless refresh_token_enabled?(resource)
35
41
 
36
- refresh_tokens_for(resource).create(token: uniq_refresh_token(resource)).token
42
+ refresh_tokens_for(resource).create(token: uniq_refresh_token(resource), expire_at: expired_refresh_token ? Time.now.utc : refresh_token_expire_at).token
37
43
  end
38
44
 
39
45
  def destroy_all_refresh_tokens(resource)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ApiGuard
4
- VERSION = '0.5.0'
4
+ VERSION = '0.6.0'
5
5
  end
data/lib/api_guard.rb CHANGED
@@ -14,6 +14,9 @@ module ApiGuard
14
14
  mattr_accessor :token_validity
15
15
  self.token_validity = 1.day
16
16
 
17
+ mattr_accessor :refresh_token_validity
18
+ self.refresh_token_validity = 2.weeks
19
+
17
20
  mattr_accessor :token_signing_secret
18
21
  self.token_signing_secret = nil
19
22
 
@@ -4,6 +4,10 @@ ApiGuard.setup do |config|
4
4
  # Validity of the JWT access token
5
5
  # Default: 1 day
6
6
  # config.token_validity = 1.day
7
+
8
+ # Validity of the refresh token
9
+ # Default: 2 weeks
10
+ # config.refresh_token_validity = 2.weeks
7
11
 
8
12
  # Secret key for signing (encoding & decoding) the JWT access token
9
13
  # Default: 'secret_key_base' from Rails secrets
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api_guard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gokul Murali
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-13 00:00:00.000000000 Z
11
+ date: 2022-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jwt
@@ -195,7 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
195
195
  - !ruby/object:Gem::Version
196
196
  version: '0'
197
197
  requirements: []
198
- rubygems_version: 3.1.2
198
+ rubygems_version: 3.0.8
199
199
  signing_key:
200
200
  specification_version: 4
201
201
  summary: Rails API authentication made easy