api_guard 0.5.0 → 0.6.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/README.md +6 -5
- data/lib/api_guard/jwt_auth/authentication.rb +18 -8
- data/lib/api_guard/jwt_auth/json_web_token.rb +3 -2
- data/lib/api_guard/jwt_auth/refresh_jwt_token.rb +9 -3
- data/lib/api_guard/version.rb +1 -1
- data/lib/api_guard.rb +3 -0
- data/lib/generators/api_guard/initializer/templates/initializer.rb +4 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 664b5fd08385f8cb690e350127d83bdeb8228264ccf179bc2a4242841e27e114
|
4
|
+
data.tar.gz: 6c94a5a7867c4ce5aa186f4f18fbfd4706b654818de791e23b8d83827e58fcf1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
[](https://rubygems.org/gems/api_guard)
|
4
|
-
[](https://github.com/Gokul595/api_guard/actions?query=workflow%3Abuild)
|
4
|
+
[](https://github.com/Gokul595/api_guard/actions?query=workflow%3Abuild-master)
|
5
5
|
[](https://codeclimate.com/github/Gokul595/api_guard/maintainability)
|
6
|
-
[](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
|
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 `
|
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
|
-
|
13
|
-
|
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
|
24
|
-
def
|
25
|
-
@
|
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
|
-
|
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
|
-
|
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).
|
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
|
-
|
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)
|
data/lib/api_guard/version.rb
CHANGED
data/lib/api_guard.rb
CHANGED
@@ -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.
|
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:
|
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.
|
198
|
+
rubygems_version: 3.0.8
|
199
199
|
signing_key:
|
200
200
|
specification_version: 4
|
201
201
|
summary: Rails API authentication made easy
|