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 +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
|
[![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
|
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
|